- // ==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);