除非清除缓存,否则注入的脚本无法正常运行



我正在重写Chrome扩展以显示v3(该死的,谷歌垄断!(

它针对某个网站,添加了一些很酷的功能。这意味着我必须注入可以访问页面上下文的脚本。该网站使用PageSpeed Insights(可能是一个旧版本,因为我怀疑他们现在使用的是eval(,它获取字符串化的代码并对其进行评估:

<script src="https://cdn.tanktrouble.com/RELEASE-2021-07-06-01/js/red_infiltration.js+messages.js.pagespeed.jc.vNQfCE2Wzx.js"></script>
<!-- ^ Contains the variables `mod_pagespeed_nEcHBi$9_H = 'function foo(){...}'` and `mod_pagespeed_gxZ81E5yX8 = 'function bar(){...}'` and the scripts below evaluate them -->
<script>eval(mod_pagespeed_nEcHBi$9_H);</script>
<script>eval(mod_pagespeed_gxZ81E5yX8);</script>

现在,我提出了一个方法,在这个方法中,我覆盖了本机eval函数,获取正在评估的字符串,生成一个哈希,并将其与一些存储的哈希进行比较。如果它们匹配,我将停止对脚本进行评估,并注入我的脚本。理论上效果良好,如果网站更新任何代码,它可以作为故障保护。

问题在于,在50%的情况下,评估发生在我覆盖eval之前,而且它似乎与缓存有关。如果我进行硬重置(Ctrl+Shift+R(,它在大多数情况下都能工作,并按预期注入脚本。但是,正常的重新加载/站点加载是不起作用的。

manifest.json

...
"content_scripts": [{
"run_at": "document_start",
"js": ["js/content.js"],
"matches": [ "*://*.tanktrouble.com/*" ]
}
], ...

content.js(内容脚本(

class GameLoader {
constructor() {
// Preload scripts, why not?
this.hasherScript = Object.assign(document.createElement('script'), {
'src': chrome.runtime.getURL('js/hasher.js'),
'type': 'module' // So we can import some utils and data later on
});
// These custom elements are a hacky method to get my chrome runtime URL into the site with element datasets.
this.extensionURL = document.createElement('tanktroubleaddons-url');
this.extensionURL.dataset.url = chrome.runtime.getURL('');
}
observe() {
return new Promise(resolve => {
const observer = new MutationObserver((mutations, observer) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.tagName === 'HEAD') {
node.insertBefore(this.extensionURL, node.firstChild); // extensionURL is used in hasherScript.js.
node.insertBefore(this.hasherScript, node.firstChild); // Inject the hash module
resolve(), observer.disconnect();
}
}
}
})
.observe(document.documentElement, { childList: true, subtree: true });
});
}
}
const loader = new GameLoader();
loader.observe();

hasher.js(this.hasherScript(

import ScriptHashes from '/config/ScriptHashes.js'; // The script hashes previously mentioned. The format is { '7pqp95akl2s': 'game/gamemanager.js' ... }
import Hasher from '/js/utils/HashAlgorithm.js'; // Quick hash algorithm
/**
Here we implement the aforementioned hacky method for the extension URL.
*/
const nodeData = document.querySelector('tanktroubleaddons-url'),
extensionURL = nodeData.dataset.url;
window.t_url = function(url) {
return extensionURL + url;
}
// Change the native eval function and generate a hash of the script being evaluated.
const proxied = eval;
const hashLength = ScriptHashes.length;
window.eval = function(code) {
if (typeof code === 'string') {
const codeHash = Hasher(code),
match = ScriptHashes.hashes[codeHash]; // Check the newly generated hash against my list of hashes. If match, it should return a path to use be used in script fetching.
if (match) {
// Synchronous script fetching with jQuery. When done, return null so original script won't be evaluated.
$.getScript(t_url('js/injects/' + match), () => {
return null;
});
}
}
return proxied.apply(this, arguments);
}

怪异的行为

当将hasherScript更改为常规脚本而不是模块时,我注意到了一些奇怪的行为。如果我排除了type参数,并将导入直接粘贴到hasher.js中,那么事情就会加载并正常工作。脚本每次都会被评估等等。这让我怀疑这是不是一个同步/异步的问题。不幸的是,我一直找不到这方面的任何信息。

提前感谢!:(

我会尝试等待DOM的加载状态,如果这还不够,并且有脚本进一步渲染页面,您可以在完全插入之前等待特定的元素/属性,但这可能还不够,所以添加了CheckState。

当您以这种方式插入它时,它大多是在DOM完全加载之前完成的,所以可能是您的问题。

/*
The DOM state should be loaded before injecting code, however this depends on the site. Calling this makes sure that all HTML/JavaScript/CSS is loaded. 
*/
window.onload = async() => {
console.log("HTML/JavaScript/CSS are considered to be loaded now");
console.log("Page may be running scripts, wait for finish!");
CheckState();
// Above is optional or may be needed. 
};
let fullyloaded;
function CheckState() {
console.log("checksite");
fullyloaded = setTimeout(() => {
/*
Sometimes the site is loaded 100% but nothing is on the page, possibly loading the rest of the site through script. In this case you can run a setTimeout
*/
if (document.querySelector('.SelectionThatTellsUsPageIsLoadedProperly')) {
InjectCode();
} else {
CheckState();
}
}, 10000);
}
function InjectCode() {
console.log("We're loaded, let's insert the code now no issues");
}

最新更新