Greasy Fork is available in English.

[ js.hook.js ]

javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持

Verzia zo dňa 05.04.2016. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name  [ js.hook.js ]
// @description javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持
// @namespace js.hook.js
// @version 0.0.3
// @author  vc1
// ==/UserScript==
/*
 *
 *  [ js.hook.js ]
 *
 *  javascript钩子
 *
 *  * 劫持方法
 *  * 伪造参数
 *  * 篡改结果
 *  * 还原劫持
 *
 *  * 2016-04-02
 *  * vc1
 *
 */
(function(name, factory) {
    if (typeof define === "function" && define.amd) {
        define(name, factory);
    } else if (typeof module === "object" && module.exports) {
        module.exports = factory();
    } else {
        this[name] = factory();
    }
})('hook', function() {
    /*
     * 入口方法
     *
     * hook(alert)
     * hook('window.alert')
     * hook(window, 'alert')
     * hook('MyOjbect.User.info.edit')
     */

    function hook() {
        'use stric'
        if (this instanceof hook) return this;

        var fn_real, // 原始方法正身
            fn_name, // 被劫持的方法名
            fn_object_name,
            fn_object; // 被劫持的方法所在对象
        // 'window.alert'
        // 'alert'
        // alert
        // window, 'alert'
        var args = Array.prototype.slice.call(arguments),
            arg = args.pop();
        fn_object = args.pop() || root;
        fn_name = arg.name;
        if (typeof arg === 'string') {
            arg = arg.split('.');
            fn_name = arg.pop();
            fn_object_name = arg.join('.');
            fn_object = eval(fn_object_name || fn_object);
        }
        fn_real = fn_object[fn_name];

        if (!(fn_object && fn_name && fn_real)) {
            console.error(arguments);
            throw new Error('hook fail');
        }

        var storage;
        if (fn_object_name) {
            storage = hook.prototype.storage[fn_object_name] = hook.prototype
                .storage[fn_object_name] || {};
        } else {
            Object.defineProperties(fn_object, {
                '__hook__': {
                    value: {},
                    enumerable: false,
                    configurable: true
                }
            });
            storage = fn_object.__hook__;
        }

        // 已经劫持过了,返回已有的钩子
        if (storage[fn_name]) {
            return storage[fn_name].exports;
        }

        var h = new hook();
        // 原始方法正身
        h.fn_real = fn_real;
        // 被劫持的方法名
        h.fn_name = fn_name;
        // 被劫持的方法所在对象,默认 window
        h.fn_object = fn_object;
        // 所在对象名称
        h.fn_object_name = fn_object_name;
        // 伪造传入参数
        h.fakeArgFn = null;
        // 伪造返回结果
        h.fakeRstFn = null;
        // 对外暴露的功能
        h.exports = {
            fake: h.fake.bind(h),
            fakeArg: h.fakeArg.bind(h),
            fakeRst: h.fakeRst.bind(h),
            off: h.off.bind(h),
            offArg: h.offArg.bind(h),
            offRst: h.offRst.bind(h),
            data: {
                fn_real: fn_real,
                fn_name: fn_name,
                fn_object_name: fn_object_name,
                fn_object: fn_object,
                get fn_puppet() {
                    return h.fn_puppet;
                },
                get fakeArgFn() {
                    return h.fakeArgFn;
                },
                get fakeRstFn() {
                    return h.fakeRstFn;
                }
            }
        };

        // 保存当前钩子
        storage[fn_name] = h;

        return h.exports;
    }

    hook.prototype.storage = {};
    var root = window || global,
        eval = root.eval;

    /*
     * 替换原始方法
     *
     * 作用等于 temp=alert; alert=function(){// your function}
     *
     * fakeFn(arguments, data)
     * 接收到的参数列表, 原始方法信息, 对象实例或原对象, 执行时的作用域
     * flag为false,等于x=fn
     */
    hook.prototype.fake = function(fakeFn, flag) {
        var data = this.exports.data;
        var puppet = eval("(function " + this.fn_real.name + "() {" +
            "data.scope = this;" + 
            (flag === false ?
            "return fakeFn.apply(this, arguments)" :
            "return fakeFn.call(this, arguments, data)"
            ) +
        "})");
        for (var prop in Object.getOwnPropertyNames(this.fn_real)) {
            puppet[prop] = this.fn_real[prop];
        }
        puppet.toLocaleString = puppet.toString = function() {
            return 'function () { [native code] }';
        };

        this.fn_puppet = puppet;
        this.fn_object[this.fn_name] = puppet;
        return this.exports;
    };

    /*
     * 在原方法前,劫持传入的参数
     *
     * fakeArg('直接替换为要传入的参数', ...)
     * fakeArg(function(原参数,){
     *     //
     *     return [修改后的参数1,2,3]
     * })
     *
     * 无返回则采用原始参数
     */
    hook.prototype.fakeArg = function(arg) {
        'use stric'
        this.__fakeArgRst__();
        this.fakeArgFn = this.__getFun__(arguments);
        return this.exports;
    };

    /*
     * 在原方法后,劫持返回的数据
     *
     * fakeRst('直接替换为要传入的参数')
     * fakeRst(function(原返回值){
     *     //
     *     return 修改后的返回值
     * })
     */
    hook.prototype.fakeRst = function(arg) {
        'use stric'
        this.__fakeArgRst__();
        this.fakeRstFn = this.__getFun__(arg);
        return this.exports;
    };


    /*
     * 开启劫持arg/rst
     */
    hook.prototype.__fakeArgRst__ = function() {
        'use stric'
        if (typeof this.fn_puppet === 'function') return;
        var t = this;
        var fakeArgRstFn = function(args, data) {
            var faked_arg = data.fakeArgFn ? data.fakeArgFn.apply(
                this, args) || args : args;
            typeof faked_arg !== 'string' && Array.prototype.slice.call(
                faked_arg).length === 0 && (faked_arg = [
                faked_arg]);
            var real_rst = data.fn_real.apply(this, faked_arg);
            var faked_rst = data.fakeRstFn ? data.fakeRstFn.call(
                this, real_rst) : real_rst;
            return faked_rst;
        };
        this.fake(fakeArgRstFn);
    };

    /*
     * 关闭劫持
     *
     * 传入参数为空:关闭前后所有劫持   hook(alert).off()
     * 传入字符串 "arg" 或 "rst":关闭对应劫持   hook(alert).off('arg')
     * 传入方法:关闭对应劫持
     *
     * 前后劫持全部关闭后,还原被 hook 的方法
     */
    hook.prototype.off = function(filter) {
        'use stric'
        if (!filter) {
            delete this.fakeArgFn;
            delete this.fakeRstFn;
        } else {
            (this.fakeArgFn === fn || filter === 'arg') && delete this.fakeArgFn;
            (this.fakeRstFn === fn || filter === 'rst') && delete this.fakeRstFn;
        }

        if (!this.fakeArgFn && !this.fakeRstFn) {
            this.fn_object[this.fn_name] = this.fn_real;
            this.fn_puppet = undefined;
            //delete this.storage[this.fn_object_name][this.fn_name];
        }

        return this.exports;
    };

    /*
     * 关闭前面的参数劫持
     *
     */
    hook.prototype.offArg = function(filter) {
        'use stric'
        filter = filter || 'arg';
        this.off(filter);
        return this.exports;
    };

    /*
     * 关闭后面的结果劫持
     *
     */
    hook.prototype.offRst = function(filter) {
        'use stric'
        filter || 'rst';
        this.off(filter);
        return this.exports;
    };


    /*
     * 直接修改参数或返回结果
     */
    hook.prototype.__getcloser__ = function(args) {
        'use stric'
        return function() {
            return args;
        };
    };
    hook.prototype.__getFun__ = function(arg) {
        'use stric'
        return typeof arg[0] == 'function' ? arg[0] : this.__getcloser__(
            arg);
    };

    return hook;
});