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

【油猴开发指南】关于Vue3的劫持处理初步方案

[复制链接]
  • TA的每日心情
    擦汗
    2024-7-16 09:20
  • 签到天数: 192 天

    [LV.7]常住居民III

    708

    主题

    5857

    回帖

    6682

    积分

    管理员

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

    积分
    6682

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

    发表于 7 天前 | 显示全部楼层 | 阅读模式

    测试网站https://www.bilibili.com/

    首先可以找到存在__vue_app__属性
    查看源码可以知道在 mount 函数中的

    mount(rootContainer, isHydrate, namespace) {
        if (!isMounted) {
        //省略
        rootContainer.__vue_app__ = app;
        return getComponentPublicInstance(vnode.component);
        }
    }

    还有一个_vnode 属性来自于mountElement函数的

    def(el, "__vnode", vnode, true);

    但是我们想在想进行全局劫持,势必要混入自己的代码,Vue3调用是createApp(rootComponent).mount(dom)
    rootContainer.__vue_app__ = app;是在挂接之后才能出现的
    我们要劫持势必要在初始化时劫持,于是可以顺着createApp找找思路

    function createApp(rootComponent, rootProps = null) {
            if (!isFunction(rootComponent)) {
              rootComponent = extend({}, rootComponent);
            }
             //省略
            return app;
    };

    其中可以看到初始化的时候有判断isFunction编译之后的会通过sfc将vue模板文件编译成对象,所以大概率会走到下面的extend函数,那我们看看extend是怎么实现的const extend = Object.assign;
    可以发现这部分有利用的机会!

    const assign = Object.assign
    let isRun = false
    Object.assign = function (...args) {
        if (args.length==2&&args[1]?.render !== undefined && !isRun) {
            let b=args[1]
            const originRender = b.render
            b.render = function (...args) {
                console.log("被执行了", args)
                return originRender.call(this, ...args)
            }
            isRun = true
        }
        return assign.call(this, ...args)
    }

    这里我利用了劫持assign可以得到根组件的render函数,控制render是因为渲染模板相对其他的属性来说可能对数据的暴露拥有更多的访问机会

    但是我们该怎么混入实例呢?根据逆向最后找到了renderComponentRoot函数的vnode.shapeFlag & 4分支,render模板上级调用如下

                render.call(
                  thisProxy,
                  proxyToUse,
                  renderCache,
                  true ? shallowReadonly(props) : props,
                  setupState,
                  data,
                  ctx
                )

    可以看到最后一个就是ctx!
    那我们就可以通过render最后一个参数找到appContext,根据appContext再对全局混入数据!
    理论建立完毕了!

    // ==UserScript==
    // @name         Vue3 Mixin Inject
    // @namespace    https://bbs.tampermonkey.net.cn/
    // @version      0.1.0
    // @description  try to take over the world!
    // @author       You
    // @match        https://www.bilibili.com/*
    // @run-at       document-start
    // @grant unsafeWindow
    // ==/UserScript==
    
    const assign = Object.assign
    let isRun = false
    Object.assign = function (...args) {
        if (args.length==2&&args[1]?.render !== undefined && !isRun) {
            let b=args[1]
            const originRender = b.render
            let isInject=false
            b.render = function (...args) {
                if (!isInject) {
                    args[5]['_'].appContext.mixins.push({
                        mounted() {
                            console.log("被创建了,实例数据",this.$props)
                        }
                    })
                    isInject = true
                }
                console.log("被执行了", args)
                return originRender.call(this, ...args)
            }
            isRun = true
        }
        return assign.call(this, ...args)
    }

    然后测试一下9e16664eacf77757d0fb2ea920061f7a.jpeg

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

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

    该用户从未签到

    1

    主题

    12

    回帖

    10

    积分

    助理工程师

    积分
    10

    油中2周年新人报道

    发表于 7 天前 | 显示全部楼层
    插眼,有空研究
    回复

    使用道具 举报

    该用户从未签到

    1

    主题

    4

    回帖

    9

    积分

    助理工程师

    积分
    9
    发表于 6 天前 | 显示全部楼层
    gg牛哇,学习了学习了
    回复

    使用道具 举报

    发表回复

    本版积分规则

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