web端的八门神器开发指南

web端的八门神器开发指南

简介

知道八门神器吗?知道的人想必已经30岁开外了,在那个移动互联网刚刚起步的时代,有很多有趣又好玩的单机游戏,之前的游戏开发还不成熟,对于金币等数值类类型的存储并咩有加密,此时八门神器出现了。

他的原理非常简单,比如你想修改金币余额,你只需要搜索当前余额的数值,可能会出现上万个地址都是该数值,不用担心,你在游戏中花一点钱,再从这上万的数值中搜索新的余额,此时数量就会大幅度减少或者只剩下一个,重复该操作直至到一个结果,那该结果的内存地址就是余额的内存地址,修改该地址的数值,即可篡改余额。

web端的八门神器开发指南

可以借鉴下类似的方式,开发一款web端适用的八门神器。

因为web开源的特性和数值的局限性,我们可以不通过检索的方式来修改具体的数值,
直接拦截类的实例方法是一个不错的主意,因为多数实例都是通过call方法来生成的,我们直接拦截call方法,通过设定一些规则,即可实现更加通用的修改器功能。

这是一款功能强大的浏览器插件,旨在帮助开发者捕捉、读取和修改网页中的对象实例。通过自定义规则,用户可以灵活地操作网页中的对象,适用于个人研究学习使用。

功能特性

  1. 实例化捕捉规则:通过自定义函数或属性数组,捕捉符合条件的对象实例。
  2. 实体读取规则:定义需要读取的对象属性,输出以供选择。
  3. 实体设置规则:修改对象实例的属性值,实现动态更新。

实现原理

插件通过content-script.js脚本注入到网页中,利用Function.prototype.call方法拦截对象实例的调用,并根据用户定义的规则进行捕捉、读取和修改。

源码如下

manifest.json

配置文件

{
    "name": "八门神器",
    "version": "1.0.0",
    "manifest_version": 2,
    "description": "八门神器",
    "icons": {
        "16": "logo.png",
        "48": "logo.png",
        "128": "logo.png"
    },
    "permissions": [
        "tabs", "*://*/*","storage"
    ],
    "browser_action": {
        "default_title": "八门神器",
        "default_icon": "logo.png",
        "default_popup":"index.html"
    },
    "web_accessible_resources": ["injected-script.js"],
    "content_scripts": [
        {
            "matches": ["*://*/*"],
            "js": ["content-script.js"],
            "all_frames": true,
            "run_at":"document_start"
        }
    ]
}

index.html

当前文件为弹出层的页面,主要用来接收用户的配置

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>说明文档</title>
    <style>
        *{padding: 0;margin: 0}
        body{width: 650px;}
        label{display: inline-block}
        label input{vertical-align: middle}
        textarea{resize: none}
        textarea:focus{outline: none}
        textarea::placeholder{color: #999}
        .tip{padding: 15px;display: flex;flex-direction: row}
        .openUrl{width: 150px;line-height: 33px;border: none;outline: none;background-color:green;color: white;height: 33px;cursor: pointer}
        .openUrl:hover{background-color: #005b00;box-shadow: 5px 5px 5px #ccc}
    </style>
</head>
<body>
<div class="tip">
    <div>
        <div style="color: red;width: 350px;margin-bottom: 10px">用前须知:本工具只限于个人研究学习使用,请勿用于其它任意非法及商业用途。</div>
        <div><b>1. </b><b>实例化捕捉规则</b></div>
        <label><textarea id="instanceCatchRule" style="width: 350px;height: 80px" placeholder="例子1自定义函数:instance.name !== void 0 && instance.age !== void 0 代表该类拥有name和age属性,instance是注入进来的变量&#10;例子2属性数组:[&quot;name&quot;,&quot;age&quot;] 缺陷是不能识别原型上的方法和继承来的属性"></textarea></label>
        <div><b>2. </b><b>实体读取规则</b></div>
        <label><textarea id="instanceReadRule" style="width: 350px;height: 80px" placeholder="例子:[name,age],代表输出name和age以供选择"></textarea></label>
        <div><b>2. </b><b>实体设置规则</b></div>
        <label><textarea id="instanceUpdateRule" style="width: 350px;height: 80px" placeholder="例子:{name:1}"></textarea></label>
    </div>
    <div style="display: flex;justify-content: center;flex-direction: column;margin-left: 45px">
        <div style="position: absolute;margin-top: -50px;" id="tip"></div>
        <button class="openUrl" id="save">设置</button>
    </div>
    <div style="position: absolute;right: 5px;bottom: 5px;color: #666">联系QQ:xxx</div>
    <script src="indexPop.js"></script>
</div>
</body>
</html>

indexPop.js

当前文件为弹出层的js,将输入的配置存储起来,给注入的js用

function init() {
    document.getElementById('save').addEventListener('click',function () {
        save();
    });
    chrome.storage.sync.get({'8-door': {instanceCatchRule:'',instanceReadRule:'',instanceUpdateRule:''}}, function(data) {
        data = data['8-door'];
        document.getElementById('instanceCatchRule').value = data.instanceCatchRule || '';
        document.getElementById('instanceReadRule').value = data.instanceReadRule || '';
        document.getElementById('instanceUpdateRule').value = data.instanceUpdateRule || '';
    });
}
function save() {
    const instanceCatchRule = document.getElementById('instanceCatchRule').value;
    const instanceReadRule = document.getElementById('instanceReadRule').value;
    const instanceUpdateRule = document.getElementById('instanceUpdateRule').value;
    chrome.storage.sync.set({'8-door': {instanceCatchRule,instanceReadRule,instanceUpdateRule}}, function() {
        tip('设置成功!刷新网页后生效');
    });
}
function tip(msg) {
    document.getElementById('tip').innerText = msg;
    setTimeout(()=>{
        document.getElementById('tip').innerText = '';
    },3000);
}
init();

content-script.js

核心逻辑,负责读取配置,拦截call方法,通过配置篡改参数用的

let door8 = {};
function viewInitFn() {
    let classArray = [];
    let showed = true;
    let timer = null;
    function exe(exp,globalData) {
        try {
            return new window.Function('globalData','data','instance','with(globalData){return ' + exp + '}')(globalData,globalData,globalData);
        }catch (e) {
            console.error(e);
            return e.toString();
        }
    }
    function parseMethods(methods) {
        let methodDefault = null;
        let methodExt = null;
        if(methods){
            for(const key in methods){
                if(!Object.prototype.hasOwnProperty.apply(methods,[key]))continue;
                const method = methods[key];
                if(/EXT$/.test(key)){
                    if(!methodExt)methodExt = {};
                    methodExt[key] = method;
                }else {
                    if(!methodDefault)methodDefault = {};
                    methodDefault[key] = method;
                }
            }
        }
        return [methodDefault,methodExt];
    }
    function mergeMethodExt(methods,methodsExt){
        for(const key in methodsExt){
            if(!Object.prototype.hasOwnProperty.call(methodsExt,[key]))continue;
            const methodExt = methodsExt[key];
            const reg = /([ABCD])?EXT$/;
            const content = key.match(reg);
            const opportunity = content ? content[1] : 'C';
            const methodName = key.replace(reg,'');
            const method = methods[methodName];
            if(!method)return;
            switch (opportunity){
                case 'A':
                    methods[methodName] = function () {
                        methodExt.apply(methods,[...arguments,method.apply(methods,[...arguments])]);
                    };
                    break;
                case 'D':
                    delete methods[methodName];
                    break;
                case 'B':
                    methods[methodName] = function () {
                        methodExt.apply(methods,[...arguments]);
                        method.apply(methods,[...arguments]);
                    };
                    break;
                case 'C':
                default:
                    methods[methodName] = methodExt;
                    break;
            }
        }
        return methods;
    }

    function merge(parentVal, childVal) {
        const [parentMethodDefault,parentMethodExt] = parseMethods(parentVal);
        const [childMethodDefault,childMethodExt] = parseMethods(childVal);
        if (parentMethodDefault){
            Object.assign(parentVal, parentMethodDefault);
        }
        if (childMethodDefault){
            Object.assign(parentVal, childMethodDefault);
        }
        if(parentMethodExt){
            mergeMethodExt(parentVal,parentMethodExt);
        }
        if(childMethodExt){
            mergeMethodExt(parentVal,childMethodExt);
        }
        return parentVal;
    }

    function initCall() {
        const call = Function.prototype.call;
        let {instanceCatchRule,instanceUpdateRule} = door8;
        if(instanceCatchRule && instanceCatchRule[0] === '['){
            try {
                instanceCatchRule = JSON.parse(instanceCatchRule);
            }catch (e) {
                console.error(e);
            }
        }else {
            instanceCatchRule = new window.Function('globalData','data','instance','with(globalData){return ' + instanceCatchRule + '}')
        }
        Function.prototype.call = function (obj,...args) {
            const push = ()=>{
                if(classArray.indexOf(obj) === -1){
                    if(instanceCatchRule && instanceUpdateRule){
                        try {
                            const data = exe(instanceUpdateRule,obj);
                            merge(obj,data)
                        }catch (err){
                            console.error(err);
                        }
                    }
                    classArray.push(obj);
                }
            };
            if(!isObject(obj)){
                return this.apply(obj,args);
            }
            if(!instanceCatchRule){
                push();
            }else if(typeof instanceCatchRule === 'function'){
                try {
                    if(instanceCatchRule(obj,obj,obj,obj)){
                        push();
                    }
                }catch (e) {

                }
            }else {
                for(let i = 0;i < instanceCatchRule.length;i++){
                    const cur = instanceCatchRule[i];
                    if(!cur || typeof cur !== 'string' || Object.getOwnPropertyNames(obj).indexOf(cur) === -1){
                        return this.apply(obj,args);
                    }
                }
                push();
            }
            return this.apply(obj,args);
        }
    }
    function initView() {
        initCall();
        setTimeout(()=>{
            renderFixed();
            timer = setInterval(function () {
                updateDom();
            },1000);
        },document.body ? 0 : 1000)
    }
    function renderFixed() {
        const html = `<div class="door-8-wrap" style="display: none">
            <style>
                .door-8-wrap{z-index:999999999;background-color: #fff;box-shadow: 0 0 5px #000;position: fixed;right: 10px;top: 50%;margin-top: -24px;line-height: 16px;padding: 10px 6px;min-width: 120px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap}
                .door-8-close{position: absolute;right: 3px;top: 3px;cursor: pointer;}
                .door-8-close:hover{color: red}
                .door-8-result{max-height: 300px;overflow-y: scroll;overflow-x: hidden}
                .door-8-result-row{border-bottom: 1px solid #d7d7d7}
                .door-8-result-col{line-height: 16px;color: #666;position: relative}
                .door-8-result-col>span{display: inline-block;max-width: 300px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;vertical-align: middle;color: #666}
                .door-8-result-col>.p-copy{color: #333}
                .door-8-tip{font-size: 12px;text-align: center;width: 100%;line-height: 14px;color: #999;margin-top: -6px}
                .door-8-copy-tip:after{content: '复制成功';transform: scale(0.7);margin-left: 3px;color: red;position: absolute;right: 0;top: 0;background-color: #fff;padding: 2px 4px}
            </style>
            <div class="door-8-tip p-copy-all" title="单击文本可直接复制(点我可复制全部,直接粘贴至excel)">单击文本可直接复制</div>
            <div class="door-8-close" id="door-8-close">X</div>
            <div id="door-8-result" class="door-8-result"></div>
        </div>`;
        let parentEle = document.getElementById('door-8-wrap');
        if(!parentEle){
            parentEle = document.createElement('div');
            parentEle.innerHTML = html;
            parentEle.children[0].id = 'door-8-wrap';
            document.body.append(parentEle.children[0]);
        }
        document.getElementById('door-8-close').addEventListener('click',function () {
            clearInterval(timer);
            document.getElementById('door-8-wrap').style.display = 'none';
            classArray = [];
        });
        document.getElementById('door-8-wrap').addEventListener('click',function (event) {
            const target = event.target;
            if(target.classList.contains('p-copy')){
                if(copy(target.innerText)){
                    tip('复制成功!',target);
                }
            }else if(target.classList.contains('p-copy-all')){
                if(copyAll()){
                    tip('复制全部成功!',target);
                }
            }
        })
    }
    function tip(msg,ele) {
        if(!ele.classList.contains('door-8-copy-tip')){
            ele.classList.add('door-8-copy-tip');
            setTimeout(()=>{
                ele.classList.remove('door-8-copy-tip');
            },1000)
        }
    }
    function updateDom() {
        const parentElement = document.getElementById('door-8-result');
        if(!parentElement)return;
        if(classArray.length === 0 || !door8.instanceReadRule){
            parentElement.innerHTML = '';
            return;
        }
        let html = ``;
        classArray.forEach(item=>{
            const instanceReadRule = door8.instanceReadRule;
            if(!instanceReadRule){
                let result = stringify(item);
                html += `<div class="door-8-result-row"><div class="door-8-result-col"><span>实例:</span><span class="p-copy" title="${htmlEncodeByRegExp(stringify(item,null,'    '))}">${result}</span></div></div>`;
                return;
            }
            let list = [];
            try {
                list = exe(instanceReadRule,item);
            }catch (e) {
                html += `<div class="door-8-result-row"><div class="door-8-result-col"><span>读取失败:</span><span class="p-copy" title="${htmlEncodeByRegExp(e.toString())}">${e.toString()}</span></div></div>`;
                return;
            }
            let tr = '';
            if(!list || Object.prototype.toString.apply(list) !== '[object Array]'){
                list = [list];
            }
            list.forEach((item,index)=>{
                tr += `<div class="door-8-result-col"><span>${index+1}.</span><span class="p-copy" title="${htmlEncodeByRegExp(stringify(item,null,'    '))}">${stringify(item)}</span></div>`;
            });
            html += `<div class="door-8-result-row">${tr}</div>`;
        });
        parentElement.innerHTML = html;
        const wrapElement = document.getElementById('door-8-wrap');
        if(wrapElement && wrapElement.style.display !== 'block'){
            wrapElement.style.display = 'block';
        }
    }
    function copyAll() {
        if(classArray.length === 0)return false;
        const parentElement = document.getElementById('door-8-result');
        if(!parentElement)return false;
        return copy([...parentElement.querySelectorAll('.door-8-result-row')].map(row=>{
            return [...row.querySelectorAll('.door-8-result-col .p-copy')].map(ele=>ele.innerText).join('\t')
        }).join('\n'))
    }
    function isObject(obj) {
        return Object.prototype.toString.apply(obj) === '[object Object]'
    }
    function stringify(data,replace,space) {
        if(typeof data === 'number' || typeof data === 'string')return data;
        try {
            return JSON.stringify(data,replace,space)
        }catch (e) {
            return e.toString();
        }
    }
    function htmlEncodeByRegExp(str) {
        if(str === null || typeof str === 'undefined')return str;
        str = typeof str !== 'string' ? String(str) : str;
        if(str.length === 0) return '';
        return str.replace(/&/g,'&amp;')
            .replace(/</g,'&lt;')
            .replace(/>/g,'&gt;')
            .replace(/ /g,'&nbsp;')
            .replace(/'/g,'&#39;')
            .replace(/[\r\n]+/g,'&#10;')
            .replace(/"/g,'&quot;');
    }
    function copy(text) {
        if(text && typeof text !== 'string'){
            try {
                text = JSON.stringify(text,null,'    ');
            }catch (e) {

            }
        }
        const textarea = document.createElement('textarea');
        textarea.style.position = 'fixed';
        textarea.style.top = '0';
        textarea.style.left = '0';
        textarea.style.width = '1px';
        textarea.style.height = '1px';
        textarea.style.padding = '0';
        textarea.style.border = 'none';
        textarea.style.outline = 'none';
        textarea.style.boxShadow = 'none';
        textarea.style.background = 'transparent';

        textarea.value = text;
        document.body.appendChild(textarea);
        textarea.select();
        let error = null;
        try {
            const successful = document.execCommand('copy');
            error = !successful;
        } catch (err) {
            console.warn('Unable to copy.');
            error = true;
        }
        document.body.removeChild(textarea);
        return !error;
    }
    initView();
}

function init() {
    chrome.storage.sync.get('8-door',(data)=>{
        setTimeout(()=>{
            door8 = data['8-door'] || {};
            const scripts=[viewInitFn];
            scripts.forEach(function (viewInitFn) {
                const script=  document.createElement("script");
                script.innerHTML = `const door8 = ${JSON.stringify(door8)};(${viewInitFn.toString()})()`;
                script.onload=function () {
                    this.parentNode.removeChild(this);
                };
                document.head.appendChild(script);
            });
            console.log(data);
        })
    })
}
init();

crx文件下载

附件里面应该有,或者通过上面的源码自行在浏览器中打包下即可

使用方法

  1. 安装插件:将插件添加到浏览器中。
  2. 设置规则:在插件的弹出页面中,输入实例化捕捉规则、实体读取规则和实体设置规则。
  3. 应用规则:点击“设置”按钮,规则将保存并生效。
  4. 查看结果:刷新网页后,插件将根据规则捕捉、读取和修改对象实例,并在页面中显示结果。

配置事例

// 实例化捕捉规则
const instanceCatchRule = 'instance.name !== void 0 && instance.age !== void 0';

// 实体读取规则
const instanceReadRule = '[name, age]';

// 实体设置规则
const instanceUpdateRule = '{name: "John", age: 30}';

能用来做啥?

读取部分视频网站全部的m3u8链接?其他的妙用等待开发者自行挖掘哈哈

注意事项

  • 本工具仅限个人研究学习使用,请勿用于非法及商业用途。
  • 使用前请仔细阅读说明文档,确保理解各项规则的含义和用法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值