相信各位经常会用到 Chrome浏览器 或者 类Chrome浏览器,这类浏览器有个非常好用的功能就是支持插件扩展,能让浏览器实现更多的玩法,而且让定制各类功能需求也变的得心应手。

今日分享一个手搓扩展的教程,这个扩展的基本功能是 防止WebRTC泄露真实IP 和实现 各标签页的自动刷新

https://www.browserscan.net/zh/webrtc 来测试是否存在WebRTC泄露

教程的所有文件尽量保存为utf-8格式

主配置

manifest.json

{
  "manifest_version": 3,
  "name": "My extension library",
  "version": "1.0",
  "description": "Welcome to my extension library",
  "permissions": ["tabs", "activeTab", "storage", "alarms", "privacy"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"],
    "run_at": "document_start",
    "all_frames": true
  }]
}

主配置中的nameversiondescription可以随意写,permissions 声明插件所需的权限,content_scripts 功能非常强大(暂定匹配所有URL)。至于插件icon不是必须的,也不是今天的重点,如果有这方面需求的可以自己在主配置中添加即可。

主脚本

background.js

//定义一个变量(是否启用WebRTC)、一个常量(判断是不是firefox)。
let pubwebrtc=false;
const isFirefox = /Firefox/.test(navigator.userAgent) || typeof InstallTrigger !== 'undefined';

//这个是关闭标签页触发的回调,主要功能是删除计时器
chrome.tabs.onRemoved.addListener((tabId) => {
	chrome.storage.local.get(["refreshTabs"], (data) => {
		let refreshTabs = data.refreshTabs || {};
		if (refreshTabs[tabId]){
			if (refreshTabs[tabId]["timer"])chrome.alarms.clear(refreshTabs[tabId]["timer"]);
			delete refreshTabs[tabId];
			chrome.storage.local.set({ refreshTabs });
		}
	});
});

//这个是设置并开启定时刷新的功能,由popup.html(交互式窗口)触发
function setRefreshTimer(tabId, interval) {
    chrome.storage.local.get(["refreshTabs"], (data) => {
        let refreshTabs = data.refreshTabs || {};
		if (!refreshTabs[tabId]) refreshTabs[tabId]={};
		if (refreshTabs[tabId]["timer"])chrome.alarms.clear(refreshTabs[tabId]["timer"]);
		refreshTabs[tabId]["timer"]="auto"+tabId;
		chrome.alarms.create(refreshTabs[tabId]["timer"], {delayInMinutes: 0, periodInMinutes: interval/60});
		refreshTabs[tabId]["interval"] = interval;
        chrome.storage.local.set({ refreshTabs });
    });
}

//这个是接收popup.html传入的消息并做相应的处理,主要功能是开/关自动刷新、启用/禁用WebRTC
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    const { action, tabId, interval } = request;
    if (action === "start_refresh") {
        setRefreshTimer(tabId, interval);
    } else if (action === "stop_refresh") {
		chrome.storage.local.get(["refreshTabs"], (data) => {
			let refreshTabs = data.refreshTabs || {};
			if(refreshTabs[tabId]){
				if(refreshTabs[tabId]["timer"]){
					chrome.alarms.clear(refreshTabs[tabId]["timer"]);
					delete refreshTabs[tabId]["timer"];
					chrome.storage.local.set({ refreshTabs });
				}
			}
		})
    } else if (action === "start_webrtc") {
		webrtcfunc(true,true);
	} else if (action === "stop_webrtc") {
		webrtcfunc(false,true);
	}
	sendResponse({ success: true });
});

//计时器主函数,主要功能是重载页面(不受其他外在因素影响)
chrome.alarms.onAlarm.addListener((alarm) => {
	if (alarm.name.substr(0,4) == "auto") {
		let tabId=parseInt(alarm.name.substr(4));
		try{chrome.tabs.reload(tabId)}catch(e){}
	}
});

//启用或禁用WebRTC后调用的功能函数
function webrtcaction() {
	chrome.storage.local.get({
		enabled: !pubwebrtc,
		eMode: isFirefox ? 'proxy_only' : 'disable_non_proxied_udp',
		dMode: 'default_public_interface_only'
	}, prefs => {
		const value = prefs.enabled ? prefs.eMode : prefs.dMode;
		chrome.privacy.network.webRTCIPHandlingPolicy.clear({}, () => {chrome.privacy.network.webRTCIPHandlingPolicy.set({value}, () => {chrome.privacy.network.webRTCIPHandlingPolicy.get({}, s => {})})});
	});
}

//开启、禁用WebRTC
function webrtcfunc(v,s){
	let webrtc = v; pubwebrtc = webrtc;
	if (s) chrome.storage.local.set({ webrtc });
	webrtcaction();
}

//在插件加载、安装、更新时触发,主要功能是初始化WebRTC
function webrtcinit(){
	chrome.storage.onChanged.addListener(() => {webrtcaction()});
	chrome.storage.local.get(["webrtc"], (data) => {
		let webrtc = data.webrtc || false;
		webrtcfunc(webrtc,false);
	});
}

//在插件加载、安装、更新时触发,主要功能是初始化计时器本地存储
function clearStoredTabs() {
	let refreshTabs={};
	chrome.storage.local.set({ refreshTabs });
}

chrome.runtime.onStartup.addListener(() => {webrtcinit(); clearStoredTabs()});
chrome.runtime.onInstalled.addListener((details) => {webrtcinit(); if (details.reason === "install") clearStoredTabs()});

交互式页面

popup.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Auto refresh</title>
    <style>
        body { width: 200px; font-family: Arial, sans-serif; text-align: center; }
        input { width: 30%; margin: 5px 0; }
        button { width: 90%; margin: 5px 0; padding: 5px; }
    </style>
</head>
<body>
    间隔 <input type="number" id="interval" value=60 min="1"> 秒,自动刷新
    <button id="onebutton"></button>
    <script src="popup.js"></script>
    <button id="webrtc" title="作用于全局"></button>
</body>
</html>

交互式页面配套的脚本文件

popup.js

//定义提示文字常量
const strs=["开始刷新", "停止刷新", "<font color=green>当前无WebRTC泄漏</font>", "<font color=red>存在WebRTC泄漏风险</font>"]

//popup.html加载完成后执行,主要功能是和用户交互,并传递相应参数给background.js
document.addEventListener("DOMContentLoaded", () => {
    const btn = document.getElementById("onebutton");
	const wtn = document.getElementById("webrtc");
    const intervalInput = document.getElementById("interval");
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs.length === 0) return;
        const tabId = tabs[0].id;
		btn.innerHTML=strs[0];
		intervalInput.focus();intervalInput.select();
        chrome.storage.local.get(["refreshTabs"], (data) => {
            let refreshTabs = data.refreshTabs || {};
			if (refreshTabs[tabId]){
				if (refreshTabs[tabId]["interval"]){
					intervalInput.value = refreshTabs[tabId]["interval"];
					intervalInput.select();
				}
				if (refreshTabs[tabId]["timer"]) {
					btn.innerHTML=strs[1];btn.setAttribute("func","stop");
				}
			}
        });
        btn.addEventListener("click", () => {
			if (btn.getAttribute("func")){
				chrome.runtime.sendMessage({ action: "stop_refresh", tabId }, (response) => {
					btn.innerHTML=strs[0];btn.removeAttribute("func");
				});
			}else{
				if (isNaN(intervalInput.value) || intervalInput.value < 1) intervalInput.value=60;
				let interval = parseFloat(intervalInput.value);
				chrome.runtime.sendMessage({ action: "start_refresh", tabId, interval }, (response) => {
					btn.innerHTML=strs[1];btn.setAttribute("func","stop");
				});
			}
        });
        chrome.storage.local.get(["webrtc"], (data) => {
            let webrtc = data.webrtc || false;
			if (webrtc){
				wtn.innerHTML=strs[3];wtn.setAttribute("func","true");
			}else wtn.innerHTML=strs[2];
        });
        wtn.addEventListener("click", () => {
			if (wtn.getAttribute("func")){
				chrome.runtime.sendMessage({ action: "stop_webrtc", tabId }, (response) => {
					wtn.innerHTML=strs[2];wtn.removeAttribute("func");
				});
			}else{
				chrome.runtime.sendMessage({ action: "start_webrtc", tabId }, (response) => {
					wtn.innerHTML=strs[3];wtn.setAttribute("func","true");
				});
			}
        });
    });
});

强大的功能脚本

(由于过于强大,所以不方便展示)
content.js

//其实这个脚本的强大与否,主要还是取决于自己想要做什么。
//理论上只要看得见的就能进行个性化操作。
(function() {

document.addEventListener('DOMContentLoaded', function() {
	document.title = "Welcome " + document.title;
});

})();

加载插件

1.将上述几个文件全部根据提示的文件名保存到本地(格式选UTF-8)
2.启动 chrome 并打开扩展页 chrome://extensions/
3.在扩展页面 启用开发者模式
4.点击 加载已解压的扩展程序 ,选择上述文件保存的目录即可

https://www.browserscan.net/zh/webrtc 再次来测试是否存在WebRTC泄露

因为考虑到实用性和教程易于理解,所以代码基本上都采用较为基础的结构书写。

扩展插件的功能很多,在于你想干什么能干什么,在什么人手里用,所以剩下的就看佬友们的个人发挥了。

以下是拓展内容

通过脚本自动抢hax机器

首先先去按照上文完成扩展的制作和加载启用
然后将 content.js 修改成类似下面这样的。

(function() {

document.addEventListener('DOMContentLoaded', function() {
	var weburl=window.location.href;
	if (weburl.indexOf('hax.co.id/create-vps')>0){
		let sid,i;
		try{sid=document.getElementsByTagName("ins"); for(i=0;i<sid.length;i++)sid[i].style.display="none"}catch(e){}
		document.getElementById("os").selectedIndex=1;
		document.getElementById("password").value="7788aabbcc"; //服务器密码
		document.getElementById("purpose").selectedIndex=5;
		var am=document.getElementsByName("agreement[]"); for (i=0;i<am.length;i++) am[i].checked=true;
		sid=document.getElementById("datacenter");
		if(sid.options.length==1){
			window.setTimeout(function(){
				window.location.reload()
			},2000+parseInt(Math.random()*3000));//数据中心为空多长时间自动刷新
		}else{
			//选择第几个服务器,默认最后一个
			sid.selectedIndex=sid.options.length-1;
			
		}
	}
});

})();

修改完content.js之后(密码部分自行修改),重新加载插件或重新打开浏览器就可以了。

先登录 https://hax.co.id/login
然后再打开
https://hax.co.id/create-vps/
页面就会开始自动刷新,如果发现有可用的数据中心就会自动停下来,然后自动填充表单。
自己仅需要完成 人机验证 和点一下 CREATE VPS 即可