您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持
// ==UserScript== // @name [ js.hook.js ] // @description javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持 // @namespace js.hook.js // @version 0.0.4 // @author vc1 // ==/UserScript== /* * * [ js.hook.js ] * * javascript钩子 * * * 劫持方法 * * 伪造参数 * * 篡改结果 * * 还原劫持 * * * 2016-10-31 * * 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.tool.calc') // [推荐]字符串形式访问路径 */ function hook() { 'use stric'; if (this instanceof hook) return this; // hook('window.tool.calc') var fn_real, // 原始方法正身 - function calc() { ... } fn_name, // 被劫持的方法名 - 'calc' fn_object, // 被劫持的方法所在对象 - window.tool fn_object_name; // 所在对象名 - 'window.tool' 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 { fn_object.__hook__ || Object.defineProperties && 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.fake_arg_fn = null; // 伪造返回结果 h.fake_rst_fn = null; // 对外暴露的功能 h.exports = { fake: bind(h.fake, h), fakeArg: bind(h.fakeArg, h), fakeArgFn: bind(h.fakeArgFn, h), fakeRst: bind(h.fakeRst, h), fakeRstFn: bind(h.fakeRstFn, h), off: bind(h.off, h), offArg: bind(h.offArg, h), offRst: bind(h.offRst, h), data: { fn_real: fn_real, fn_name: fn_name, fn_object_name: fn_object_name, fn_object: fn_object, fn_puppet: h.fn_puppet, fakeArgFn: h.fake_arg_fn, fakeRstFn: h.fake_rst_fn } }; /*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; // 模拟Function.bind var bind = function(fn, scope) { return function() { return fn.apply(scope, arguments) } } /* * 替换原始方法 * * 作用等于 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 this.fn_real) { if (obj.hasOwnProperty(k)) { puppet[prop] = this.fn_real[prop]; } } puppet.toLocaleString = puppet.toString = function() { return 'function () { [native code] }'; }; this.fn_puppet = this.exports.fn_puppet = puppet; this.fn_object[this.fn_name] = puppet; return this.exports; }; /* * 在原方法前,劫持传入的参数 * * fakeArg('直接替换为要传入的参数', '2', 3...) * */ hook.prototype.fakeArg = function() { 'use stric'; this.__fakeArgRst__(); this.fake_arg_fn = this.exports.data.fakeArgFn = __getFun__( arguments); return this.exports; }; /* * fakeArgFn(function(原参数1, 2, 3...){ * return [修改后的参数1,2,3] * // 无返回(undefinded)则使用原始参数 * // 清空传入参数可以返回一个空数组 return [] * }) * */ hook.prototype.fakeArgFn = function(fn) { 'use stric'; this.__fakeArgRst__(); this.fake_arg_fn = this.exports.data.fakeArgFn = fn; return this.exports; }; /* * 在原方法后,劫持返回的数据 * * fakeRst('直接替换为要返回的结果') * */ hook.prototype.fakeRst = function(arg) { 'use stric'; this.__fakeArgRst__(); this.fake_rst_fn = this.exports.data.fakeRstFn = __getFun__( arg); return this.exports; }; /* * fakeRstFn(function(原返回值){ * return 修改后的返回值 * // 无返回(undefinded)则使用原始参数 * }) * */ hook.prototype.fakeRstFn = function(fn) { 'use stric'; this.__fakeArgRst__(); this.fake_rst_fn = this.exports.data.fakeRstFn = fn; 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; faked_arg = faked_arg === undefined ? args : faked_arg; 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; faked_rst = faked_rst === undefined ? real_rst : faked_rst; return faked_rst; }; this.fake(fakeArgRstFn, true); }; /* * 关闭劫持 * * 传入参数为空:关闭前后所有劫持 hook(alert).off() * 传入字符串 "arg" 或 "rst":关闭对应劫持 hook(alert).off('arg') * 传入方法:关闭对应劫持 * * 前后劫持全部关闭后,还原被 hook 的方法 */ hook.prototype.off = function(filter) { 'use stric';; (!filter || filter === 'arg') && (this.fake_arg_fn = this.exports .data.fakeArgFn = null); (!filter || filter === 'rst') && (this.fake_rst_fn = this.exports .data.fakeRstFn = null); if (!this.fake_arg_fn && !this.fake_rst_fn) { 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; }; /* * 直接修改参数或返回结果 */ var __getFun__ = function(args) { 'use stric'; return /*typeof args[0] == 'function' ? args[0] :*/ function() { return args; }; }; return hook; }); // 效果测试 /* window.tool = { calc: function(msg, n) { console.warn('calc收到参数:' + msg + ', ' + n); var r = n * n; console.warn('calc结果:' + r); return r; } } console.clear(); console.info('一个计算器:'); console.group('原始方法:\ntool.calc'); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); console.log('\n'); console.group("劫持后:\nhook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst(function(right){\n" + " console.info('fakeRst:计算器结果返回:' + right);\n " + " return '<(ˉ^ˉ)> 告诉你坏了'\n" + "}") hook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRstFn(function(right){ console.info('fakeRst:计算器返回的结果:' + right); return '<(ˉ^ˉ)> 告诉你坏了' }); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); console.log('\n'); console.group("还原后:\nhook('window.tool.calc').off();"); hook('window.tool.calc').off(); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); */ /* function print(msg){ document.write((msg||'<br>') + '<br>'); } window.tool = { calc: function(msg, n) { print('calc收到参数:' + msg + ', ' + n); var r = n * n; print('calc结果:' + r); return r; } } print('一个计算器:'); print('原始方法:\ntool.calc'); print(tool.calc); print('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); print('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); print(); print('\n'); print("劫持后:\nhook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst(function(right){\n" + " print('fakeRst:计算器结果返回:' + right);\n " + " return '<(ˉ^ˉ)> 告诉你坏了'\n" + "}"); hook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRstFn(function(right){ print('fakeRst:计算器返回的结果:' + right); return '<(ˉ^ˉ)> 告诉你坏了' }); print(tool.calc); print('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); print('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); print(); print('\n'); print("还原后:\nhook('window.tool.calc').off();"); hook('window.tool.calc').off(); print(tool.calc); print('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); print('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); print(); */