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

[油猴脚本开发指南]Fetch劫持的第二种方式

[复制链接]
  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    620

    主题

    5087

    回帖

    5960

    积分

    管理员

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

    积分
    5960

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-9-22 17:12:55 | 显示全部楼层 | 阅读模式

    前言

    之前我们已经谈过了fetch劫持,但是由于那个代码相对较为复杂,cxxjackie提供了一个相对简单的方式,并未使用proxy劫持,我们这节课来分析一下。

    let oldfetch = fetch;
    function fuckfetch() {
        return new Promise((resolve, reject) => {
            oldfetch.apply(this, arguments).then(response => {
                const oldJson = response.json;
                response.json = function() {
                    return new Promise((resolve, reject) => {
                        oldJson.apply(this, arguments).then(result => {
                            result.hook = 'success';
                            resolve(result);
                        });
                    });
                };
                resolve(response);
            });
        });
    }
    window.fetch = fuckfetch;

    oldfetch保存了原fetch的引用

    这时候我们对window.fetch挂载成我们的劫持函数,fuckfetch

    因为fetch需要返回一个promise,所以这里我们通过

    return new Promise((resolve, reject) => {})

    包裹了一下,并且在原fetch函数内调用,并获取返回内容,对其进行一些处理并resolve返回过去。

    附注:函数一旦返回一个Promise,我们只考虑输出相同的结果即可,而无须考虑Promise内处理过程的一致性。

            oldfetch.apply(this, arguments).then(response => {
                const oldJson = response.json;
                response.json = function() {
                    return new Promise((resolve, reject) => {
                        oldJson.apply(this, arguments).then(result => {
                            result.hook = 'success';
                            resolve(result);
                        });
                    });
                };
                resolve(response);
            });

    这里我们使用fetch的原函数,通过apply更改了this指针至自身,并且传入了参数。注意这点有一个需要注意的是,我们劫持函数的时候,由劫持函数调用原函数的过程中一定要使用call/apply进行修改this指针,来符合原来的调用过程。

    在then后则是我们处理response的过程

                const oldJson = response.json;
                response.json = function() {
                    return new Promise((resolve, reject) => {
                        oldJson.apply(this, arguments).then(result => {
                            result.hook = 'success';
                            resolve(result);
                        });
                    });
                };

    这部分代码是针对某些特定的函数进行过滤,我们可以对网页进行分析以及调试,或去返回内容进行查看,来判断调用了哪些函数。

    这里以json为例进行劫持

    首先保存了原json的引用

    然后修改json属性为一个劫持函数

    由于json返回的是一个promise对象,所以我们这里也需要返回一个promise

    在promise内依然是对其原json函数进行调用,并修改了this指向以及参数,最后对其结果进行一定的修改,然后通过resolve(result)进行返回。

    那么这节课我们就了解到了一个相对较为轻量的fetch劫持方法!

    结语

    撒花~

    已有1人评分好评 油猫币 贡献 理由
    懒男孩 + 1 + 5 + 1 ggnb!

    查看全部评分 总评分:好评 +1  油猫币 +5  贡献 +1 

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
  • TA的每日心情
    慵懒
    2023-5-5 13:37
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    117

    主题

    404

    回帖

    708

    积分

    版主

    积分
    708

    油中2周年油中3周年

    发表于 2021-9-22 17:15:01 | 显示全部楼层
    ggnba!!!
    提及少年一词,应与平庸相斥!微信公众号——智家乐享
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    620

    主题

    5087

    回帖

    5960

    积分

    管理员

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

    积分
    5960

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-9-22 17:24:38 | 显示全部楼层

    好久没看到哥哥了
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-3 00:00
  • 签到天数: 117 天

    [LV.6]常住居民II

    27

    主题

    588

    回帖

    521

    积分

    专家

    积分
    521

    油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2021-9-22 21:30:03 | 显示全部楼层
    按键精灵您肯定知道,他们很多导师都有自己的一套或几套插件,就是一个函数集啦。这对新手非常友好。甚至不需要去了解实现的原理。拖过来直接就可以用。能不能有针对性的做一些这种类似的东西,函数命名规范化。比如网络劫持类,这种叫釜底抽薪式。XMLHttpRequest那个叫红杏出墙式,等等
    入驻爱发电 让这世界充满爱 https://afdian.net/a/vpannice
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    620

    主题

    5087

    回帖

    5960

    积分

    管理员

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

    积分
    5960

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-9-23 05:28:21 | 显示全部楼层
    脚本体验师001 发表于 2021-9-22 21:30
    按键精灵您肯定知道,他们很多导师都有自己的一套或几套插件,就是一个函数集啦。这对新手非常友好。甚至不 ...

    这个是有计划的!其实
    准备以后有机会慢慢搞,先集中精力给教程搞完...
    呜呜呜,教程东西太他妈多了....
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    9

    回帖

    10

    积分

    荣誉开发者

    积分
    10

    荣誉开发者

    发表于 2021-11-30 20:35:22 | 显示全部楼层
    脚本体验师001 发表于 2021-9-22 21:30
    按键精灵您肯定知道,他们很多导师都有自己的一套或几套插件,就是一个函数集啦。这对新手非常友好。甚至不 ...

    那是因为按键有自己的框架和编辑器,而油猴是开放的,源码也是开放的,如果在scriptcat基础上运行,倒是可以封装你提到的库或者API,但是需要时间和资金
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    22 小时前
  • 签到天数: 603 天

    [LV.9]以坛为家II

    30

    主题

    523

    回帖

    1379

    积分

    荣誉开发者

    积分
    1379

    荣誉开发者新人进步奖油中2周年生态建设者新人报道挑战者 lv2油中3周年喜迎中秋

    发表于 2022-7-1 16:59:22 | 显示全部楼层
    //劫持前fetch
    window.hook_fetch=window.fetch
    
    //劫持后fetch
    window.fetch= async function(...args){ 
        let result = await hook_fetch(...args).then((e)=>{
            let res =e.clone() //克隆response
            e.text().then((ee)=>{
                console.log("第一次text()"+ee)
                //对劫持数据进行操作
            })
            return res //返回克隆的response
        })
        return  result
    }
    
    //网站"原生"fetch
    fetch("abc").then((e)=>{
        e.text().then((ee)=>{
            console.log("第二次text()"+ee)
            //网站"原生"操作
        })
    })

    话说response的stream虽然只能读一次,但是response本身能用.clone()的方式被克隆(这不是扯淡吗 那stream只读一次有什么用)
    感觉可以利用response.clone()克隆response,直接把原始response复制一份给网站原生的then,然后我们对原本的response就可以爱怎么操作怎么操作了
    随便写了个代码,GG看看有没有什么可以改进的(我异步不是很熟练 应该是这么写的吧)

    fetch.png

    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    857

    回帖

    1356

    积分

    荣誉开发者

    积分
    1356

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-7-1 20:40:10 | 显示全部楼层
    steven026 发表于 2022-7-1 16:59
    [md]```
    //劫持前fetch
    window.hook_fetch=window.fetch

    你这个没有起到修改response的效果啊,只是返回了一份复制品,网站读到的response还是原来的,这个劫持的目的就是让返回值发生变化。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    22 小时前
  • 签到天数: 603 天

    [LV.9]以坛为家II

    30

    主题

    523

    回帖

    1379

    积分

    荣誉开发者

    积分
    1379

    荣誉开发者新人进步奖油中2周年生态建设者新人报道挑战者 lv2油中3周年喜迎中秋

    发表于 2022-7-1 22:02:38 | 显示全部楼层

    cxxjackie 发表于 2022-7-1 20:40

    你这个没有起到修改response的效果啊,只是返回了一份复制品,网站读到的response还是原来的,这个劫持的 ...

    //劫持前fetch
    window.hook_fetch=window.fetch
    
    //劫持后fetch
    window.fetch= async function(...args){ 
        return await hook_fetch(...args).then((e)=>{
            let res =e.clone() //克隆response
            e.text().then((ee)=>console.log("原始response内容",ee))
            res.text=()=>{return new Promise((resolve)=>{resolve("hooked")})} //修改返回内容
            return res //返回克隆并修改的response
        })
    }
    
    //网站"原生"fetch
    fetch("https://bbs.tampermonkey.net.cn/").then((e)=>{
        e.text().then((ee)=>{
            console.log("修改后response内容",ee)
            //网站"原生"操作
        })
    })

    要修改返回内容的话,我写成了这个样子,GG看下这样会有什么问题吗?
    fetch.png

    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    857

    回帖

    1356

    积分

    荣誉开发者

    积分
    1356

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-7-1 22:24:52 | 显示全部楼层
    steven026 发表于 2022-7-1 22:02
    [md]```
    //劫持前fetch
    window.hook_fetch=window.fetch

    这样是覆盖,不能在原数据的基础上修改,或者把e.text().then挪到Promise里面,先取得原数据再修改返回就好了,不过这样就跟这篇的思路完全一样了,而且多克隆了一个response,可能产生额外的性能开销。
    回复

    使用道具 举报

    发表回复

    本版积分规则

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