#1 - 2019-5-25 08:56
烈之斩 (V1046-R MAHORO)
最近写了个很简单的US脚本是是劫持某个网站的global函数来实现一些自己想要的功能。

写法来自这里:https://stackoverflow.com/a/21274652

完整代码就不贴了,最小化之后就是这样:

// ==UserScript==
// @match        https://moca-news.net/*
// ==/UserScript==

addJS_Node(image_load_body);
function addJS_Node(text, s_URL, funcToRun, runOnLoad) {
    var D = document;
    var scriptNode = D.createElement('script');
    if (runOnLoad) {
        scriptNode.addEventListener("load", runOnLoad, false);
    }
    scriptNode.type = "text/javascript";
    if (text) scriptNode.textContent = text;
    if (s_URL) scriptNode.src = s_URL;
    if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
    var targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
    targ.appendChild(scriptNode);
}

function image_load_body(art_id, img_id, _mode, _retry) {
    console.log('image_load_body replaced');
}


我的问题在于,这个脚本在
https://moca-news.net/article/20 ... 530a_/image009.html
这类网页工作完全正常,
但是在 诸如
https://moca-news.net/imgsearch/ ... tid=2013110616530a_
这类网页里,我修改过之后的function虽然最后还是可以成功inject(在网页完全load完之后console输入image_load_body显示的是我的修改版而不是原始),但是实际运行的还是在 /large_image.js 里的image_load_body,而不是我的修改版(控制台不会显示'image_load_body replaced')。

这俩网页虽然不太一样,但是感觉原理都差不多,不知道为什么一个行一个不行。改了半天run-at 也没用。

求教!
#2 - 2019-5-25 09:55
因为 https://moca-news.net/article/20 ... 530a_/image009.html 在调用 image_load_body 之前有个 HTTP 请求,时间轴是这样的
网页加载 -> 发起 HTTP 请求 -> user script 劫持 image_load_body -> HTTP 请求完成 -> 调用被劫持的 image_load_body

https://moca-news.net/imgsearch/ ... tid=2013110616530a_ 没有这个 HTTP 请求,时间轴就是
网页加载 -> 调用 image_load_body -> user script 劫持 image_load_body

如果把 run-at 改成 document-start 的话,只会被 large_image.js 里的定义覆盖

我实验了在 document-start 的时候 Object.define(window, 'image_load_body', { value: xxx, writable: false }),但是会导致 large_image.js 里的 function 定义报错,如果在 user script 里把 large_image.js 里报错导致没执行的代码手动执行一次的话或许会有效
#2-1 - 2019-5-25 11:39
烈之斩
多谢,也就是说正常来说,无法做到在网页自带的脚本“加载”和“运行”之间插入user script?
#3 - 2019-5-25 11:03
(情更怯)
我的思路是这样的: 在 文档加载完成的时候注入脚本。这个脚本先把 会执行 image_load_body  script 标签 移除 DOM 树,然后  eval.   借鉴了一下 ls 的 Object.defineProperty 的思路。

另外我本来想 移除所以 body 里面的 script,然后 eval 执行的。但是有个问题没处理好。

PS: LZ 使用的方法的方向不一定对的。可能有更好的方法。


// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        https://moca-news.net/imgsearch/*
// @grant        none
// @run-at document-end
// ==/UserScript==

(function() {
    'use strict';
const changeScriptExe = `
function image_load_body(art_id, img_id, _mode, _retry) {
    console.log('image_load_body replaced');
}
Object.defineProperty(window, 'image_load_body', { value: image_load_body, writable: false })
    var aa = Array.prototype.slice.call(document.body.getElementsByTagName('script'));
aa = []
    aa.forEach((elem,index) =>{
        elem.remove()
    });
var targetScript = document.querySelector('#resize-button').nextElementSibling;
targetScript.remove();
    setTimeout(()=> {

eval(targetScript.innerText);
console.log('1111111111');
       aa.forEach((elem,index) =>{
eval(elem.innerText);
        //document.body.appendChild(elem);
    });
    }, 1000);
`
addJS_Node(changeScriptExe);
//addJS_Node(image_load_body);

function addJS_Node(text, s_URL, funcToRun, runOnLoad) {

    var D = document;

    var scriptNode = D.createElement('script');

    if (runOnLoad) {

        scriptNode.addEventListener("load", runOnLoad, false);

    }

    scriptNode.type = "text/javascript";

    if (text) scriptNode.textContent = text;

    if (s_URL) scriptNode.src = s_URL;

    if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;


    targ.appendChild(scriptNode);

}




})();
#3-1 - 2019-5-25 11:42
烈之斩
首先多谢

我试了你的脚本,虽然成功替换了image_load_body并执行了一次,但是原始的image_load_body也被执行了(否则图片应该加载不出来)。