您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持
当前为
// ==UserScript== // @name [ js.hook.js ] // @description javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持 // @namespace js.hook.js // @version 0.0.2 // @author vc1 // ==/UserScript== ; (function (name, ctx) { /* * * [ js.hook.js ] * * javascript钩子 * * * 劫持方法 * * 伪造参数 * * 篡改结果 * * 还原劫持 * * * 2015-11-24 * * vc1 * */ // 'use stric' var definition = (function () { /* * 入口方法 * * hook(alert) * hook('window.alert') * hook('MyOjbect.User.info.edit') */ function hook() { if (this instanceof hook) { return hook.prototype.__init__.apply(this, arguments); } var t = hook.prototype.__getTarget__.apply(null, arguments); // 已经劫持过了,返回已有的钩子 if (hook.prototype.storage[t.fn_object_name] && hook.prototype.storage[t.fn_object_name][t.fn_name]) { return hook.prototype.storage[t.fn_object_name][t.fn_name].exports; } return new hook(t.fn_object, t.fn_object_name, t.fn_name, t.fn_real); } hook.prototype.storage = {}; var eval = window.eval; hook.prototype.__init__ = function (fn_object, fn_object_name, fn_name, fn_real) { // 原始方法正身 this.fn_real = fn_real; // 被劫持的方法名 this.fn_name = fn_name; // 被劫持的方法所在对象,默认 window this.fn_object = fn_object; // 所在对象名称 this.fn_object_name = fn_object_name; // 伪造传入参数 this.fakeArgFn = null; // 伪造返回结果 this.fakeRstFn = null; // 对外暴露的功能 this.exports = { fake: this.fake.bind(this), fakeArg: this.fakeArg.bind(this), fakeRst: this.fakeRst.bind(this), off: this.off.bind(this), offArg: this.offArg.bind(this), offRst: this.offRst.bind(this), }; var t = this; this.exports_var = Object.defineProperties({}, { 'fn_real': { value: fn_real, enumerable: true }, 'fn_name': { value: fn_name, enumerable: true }, 'fn_object_name': { value: fn_object_name, enumerable: true }, 'fn_object': { value: fn_object, enumerable: true }, fakeArgFn: { get: function () { return t.fakeArgFn; } }, fakeRstFn: { get: function () { return t.fakeRstFn; } } }); // 保存当前钩子 this.storage[fn_object_name] = this.storage[fn_object_name] || {}; this.storage[fn_object_name][fn_name] = this; return this.exports; }; // 支持多种输入形式 hook.prototype.__getTarget__ = function () { var fn_real, // 原始方法正身 fn_name = 'alert', // 被劫持的方法名 fn_object_name = "Window", fn_object = window; // 被劫持的方法所在对象,默认 window if (arguments.length === 1) { var arg = arguments[0]; // 传入字符串 if (arg.__proto__ === String.prototype) { var dotidx = arg.lastIndexOf('.'); if (~dotidx) { // 'window.alert' fn_object_name = arg.slice(0, dotidx); fn_object = eval(fn_object_name); fn_name = arg.slice(dotidx + 1); fn_real = fn_object[fn_name]; } else { // 'alert' fn_name = arg; } } else { // 传入一个方法,所在对象默认 window fn_real = arg; fn_name = fn_real.name; } } else if (arguments.length == 2) { // 不推荐 fn_real = fn_object[fn_name]; fn_name = arguments[1]; fn_object = arguments[0]; fn_object_name = fn_object.constructor.name; } if (!(fn_object && fn_name && fn_real)) { console.error(fn_object); console.error(fn_object_name); console.error(fn_name); console.error(fn_real); throw new Error('hook fail'); } return { 'fn_real': fn_real, 'fn_name': fn_name, 'fn_object': fn_object, 'fn_object_name': fn_object_name, }; }; /* * 替换原始方法 * * 作用等于 temp=alert; alert=function(){// your function} * * fakeFn(arguments, t.exports_var, scope, this) * 接收到的参数列表, 原始方法信息, 对象实例或原对象, 执行时的作用域 * */ hook.prototype.fake = function (fakeFn) { var t = this; var puppet = eval("(function " + this.fn_real.name + "() {" + "var scope = this instanceof t.fn_object.constructor ? this :" + " t.fn_object;" + "return fakeFn.call(scope, arguments, t.exports_var.fn_real, scope, t.exports_var, this);" + "})"); for (var prop in this.fn_real) { puppet[prop] = this.fn_real[prop]; } puppet.toString = function () { return 'function ' + t.fn_real.name + '() { [native code] }'; }; this.fn_object[this.fn_name] = puppet; return this.exports; }; /* * 在原方法前,劫持传入的参数 * * fakeArg('直接替换为要传入的参数', ...) * fakeArg(function(原参数,){ * // * return [修改后的参数] * }) * * 无返回则采用原始参数 */ hook.prototype.fakeArg = function (arg) { this.__fakeArgRst__(); this.fakeArgFn = this.__getFun__(arguments); return this.exports; }; /* * 在原方法后,劫持返回的数据 * * fakeRst('直接替换为要传入的参数') * fakeRst(function(原返回值){ * // * return 修改后的返回值 * }) */ hook.prototype.fakeRst = function (arg) { this.__fakeArgRst__(); this.fakeRstFn = this.__getFun__(arg); return this.exports; }; /* * 开启劫持arg/rst */ hook.prototype.__fakeArgRst__ = function () { var t = this; var fakeArgRstFn = function (args, fn_real, scope, t, raw_this) { var faked_arg = t.fakeArgFn ? t.fakeArgFn.apply(scope, args) || args : args; faked_arg && !Array.isArray(faked_arg) && (!faked_arg.hasOwnProperty('callee') && !faked_arg.hasOwnProperty('length')) && (faked_arg = [faked_arg]); var real_rst = t.fn_real.apply(scope, faked_arg); var faked_rst = t.fakeRstFn ? t.fakeRstFn.call(scope, 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) { if (!filter) { delete this.fakeArgFn; delete this.fakeRstFn; } else if (typeof filter === 'function' || filter.__proto__ === String.prototype) { (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; //delete this.storage[this.fn_object_name][this.fn_name]; } return this.exports; }; /* * 关闭前面的参数劫持 * */ hook.prototype.offArg = function (filter) { filter = filter || 'arg'; this.off(filter); return this.exports; }; /* * 关闭后面的结果劫持 * */ hook.prototype.offRst = function (filter) { filter || 'rst'; this.off(filter); return this.exports; }; /* * 直接修改参数或返回结果 */ hook.prototype.__getcloser__ = function (args) { return function () { return args; }; }; hook.prototype.__getFun__ = function (arg) { return typeof arg[0] == 'function' ? arg[0] : this.__getcloser__(arg); }; return hook; }); //检测上下文环境是否为AMD或CMD var hasDefine = typeof define === 'function', // 检测上下文环境是否为Node hasExports = typeof module !== 'undefined' && typeof module !== 'function' && module.exports; if (!name) { return definition(); } else if (ctx) { // 设置环境后则挂载到此对象 /* void function(name, ctx){ ... }('hook', window); */ ctx[name] = definition(); } else if (hasDefine) { //AMD环境或CMD环境 define(name, definition); } else if (hasExports) { //定义为普通Node模块 module.exports = definition(); } return definition(); // 效果演示 /* window.tool = { say: '一个计算器开始工作了:', calc: function (msg, n) { console.log(this.say); console.warn('calc收到参数:' + msg + ', ' + n); var r = n * n; console.warn('calc结果:' + r); return r; } } var hook = window.js_hook_js; console.clear(); console.info('一个计算器:'); console.group('原始方法:\n\ntool.calc'); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); console.log('\n'); console.group("劫持后:\n\nhook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst('<(ˉ^ˉ)> 告诉你坏了');"); hook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst('<(ˉ^ˉ)> 告诉你坏了'); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); console.log('\n'); console.group("还原后:\n\nhook('window.tool.calc').off();"); hook('window.tool.calc').off(); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42)); console.groupEnd(); console.log('\n'); console.group("替换:\n\nhook('window.tool.calc').fake(function(){...})"); hook('window.tool.calc').fake(function (args, fn_real, scope, h, raw_scope) { console.log('调用者:' + args.callee.caller); console.log('this:'); console.log(this); console.log('args:'); console.log(args); console.log('fn_real:'); console.log(fn_real); console.log('scope:'); console.log(scope); console.log('h:'); console.log(h); console.log('raw_scope:'); console.log(raw_scope); return fn_real.apply({ say: '怎么还是那个计算器:' }, args); }); console.log(tool.calc); console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42); console.info('接收到的结果:' + (function calc_caller() { return tool.calc.call({ say: '换个计算器吧' }, '专注于计算平方的计算器', 42); })()); console.groupEnd(); console.log('\n'); */ })('js_hook_js', window);