[ js.hook.js ]

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

目前为 2015-11-24 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name [ js.hook.js ]
  3. // @description javascript钩子; 劫持方法/伪造参数/篡改结果/还原劫持
  4. // @namespace js.hook.js
  5. // @version 0.0.2
  6. // @author vc1
  7. // ==/UserScript==
  8.  
  9.  
  10.  
  11. ;
  12. (function (name, ctx) {
  13.  
  14. /*
  15. *
  16. * [ js.hook.js ]
  17. *
  18. * javascript钩子
  19. *
  20. * * 劫持方法
  21. * * 伪造参数
  22. * * 篡改结果
  23. * * 还原劫持
  24. *
  25. * * 2015-11-24
  26. * * vc1
  27. *
  28. */
  29.  
  30. // 'use stric'
  31.  
  32. var definition = (function () {
  33.  
  34. /*
  35. * 入口方法
  36. *
  37. * hook(alert)
  38. * hook('window.alert')
  39. * hook('MyOjbect.User.info.edit')
  40. */
  41. function hook() {
  42. if (this instanceof hook) {
  43. return hook.prototype.__init__.apply(this, arguments);
  44. }
  45.  
  46. var t = hook.prototype.__getTarget__.apply(null, arguments);
  47.  
  48. // 已经劫持过了,返回已有的钩子
  49. if (hook.prototype.storage[t.fn_object_name] && hook.prototype.storage[t.fn_object_name][t.fn_name]) {
  50. return hook.prototype.storage[t.fn_object_name][t.fn_name].exports;
  51. }
  52.  
  53. return new hook(t.fn_object, t.fn_object_name, t.fn_name, t.fn_real);
  54. }
  55.  
  56. hook.prototype.storage = {};
  57. var eval = window.eval;
  58.  
  59. hook.prototype.__init__ = function (fn_object, fn_object_name, fn_name, fn_real) {
  60. // 原始方法正身
  61. this.fn_real = fn_real;
  62. // 被劫持的方法名
  63. this.fn_name = fn_name;
  64. // 被劫持的方法所在对象,默认 window
  65. this.fn_object = fn_object;
  66. // 所在对象名称
  67. this.fn_object_name = fn_object_name;
  68. // 伪造传入参数
  69. this.fakeArgFn = null;
  70. // 伪造返回结果
  71. this.fakeRstFn = null;
  72. // 对外暴露的功能
  73. this.exports = {
  74. fake: this.fake.bind(this),
  75. fakeArg: this.fakeArg.bind(this),
  76. fakeRst: this.fakeRst.bind(this),
  77. off: this.off.bind(this),
  78. offArg: this.offArg.bind(this),
  79. offRst: this.offRst.bind(this),
  80. };
  81.  
  82. var t = this;
  83. this.exports_var = Object.defineProperties({},
  84. {
  85. 'fn_real': {
  86. value: fn_real,
  87. enumerable: true
  88. },
  89. 'fn_name': {
  90. value: fn_name,
  91. enumerable: true
  92. },
  93. 'fn_object_name': {
  94. value: fn_object_name,
  95. enumerable: true
  96. },
  97. 'fn_object': {
  98. value: fn_object,
  99. enumerable: true
  100. },
  101. fakeArgFn: {
  102. get: function () {
  103. return t.fakeArgFn;
  104. }
  105. },
  106. fakeRstFn: {
  107. get: function () {
  108. return t.fakeRstFn;
  109. }
  110. }
  111. });
  112. // 保存当前钩子
  113. this.storage[fn_object_name] = this.storage[fn_object_name] || {};
  114. this.storage[fn_object_name][fn_name] = this;
  115.  
  116. return this.exports;
  117. };
  118.  
  119. // 支持多种输入形式
  120. hook.prototype.__getTarget__ = function () {
  121. var fn_real, // 原始方法正身
  122. fn_name = 'alert',
  123. // 被劫持的方法名
  124. fn_object_name = "Window",
  125. fn_object = window; // 被劫持的方法所在对象,默认 window
  126. if (arguments.length === 1) {
  127. var arg = arguments[0];
  128. // 传入字符串
  129. if (arg.__proto__ === String.prototype) {
  130. var dotidx = arg.lastIndexOf('.');
  131. if (~dotidx) { // 'window.alert'
  132. fn_object_name = arg.slice(0, dotidx);
  133. fn_object = eval(fn_object_name);
  134. fn_name = arg.slice(dotidx + 1);
  135. fn_real = fn_object[fn_name];
  136. } else { // 'alert'
  137. fn_name = arg;
  138. }
  139. } else { // 传入一个方法,所在对象默认 window
  140. fn_real = arg;
  141. fn_name = fn_real.name;
  142. }
  143. } else if (arguments.length == 2) { // 不推荐
  144. fn_real = fn_object[fn_name];
  145. fn_name = arguments[1];
  146. fn_object = arguments[0];
  147. fn_object_name = fn_object.constructor.name;
  148. }
  149.  
  150. if (!(fn_object && fn_name && fn_real)) {
  151. console.error(fn_object);
  152. console.error(fn_object_name);
  153. console.error(fn_name);
  154. console.error(fn_real);
  155. throw new Error('hook fail');
  156. }
  157.  
  158. return {
  159. 'fn_real': fn_real,
  160. 'fn_name': fn_name,
  161. 'fn_object': fn_object,
  162. 'fn_object_name': fn_object_name,
  163. };
  164. };
  165.  
  166. /*
  167. * 替换原始方法
  168. *
  169. * 作用等于 temp=alert; alert=function(){// your function}
  170. *
  171. * fakeFn(arguments, t.exports_var, scope, this)
  172. * 接收到的参数列表, 原始方法信息, 对象实例或原对象, 执行时的作用域
  173. *
  174. */
  175. hook.prototype.fake = function (fakeFn) {
  176. var t = this;
  177. var puppet = eval("(function " + this.fn_real.name + "() {" +
  178. "var scope = this instanceof t.fn_object.constructor ? this :" +
  179. " t.fn_object;" +
  180. "return fakeFn.call(scope, arguments, t.exports_var.fn_real, scope, t.exports_var, this);" +
  181. "})");
  182. for (var prop in this.fn_real) {
  183. puppet[prop] = this.fn_real[prop];
  184. }
  185. puppet.toString = function () {
  186. return 'function ' + t.fn_real.name + '() { [native code] }';
  187. };
  188. this.fn_object[this.fn_name] = puppet;
  189. return this.exports;
  190. };
  191.  
  192. /*
  193. * 在原方法前,劫持传入的参数
  194. *
  195. * fakeArg('直接替换为要传入的参数', ...)
  196. * fakeArg(function(原参数,){
  197. * //
  198. * return [修改后的参数]
  199. * })
  200. *
  201. * 无返回则采用原始参数
  202. */
  203. hook.prototype.fakeArg = function (arg) {
  204. this.__fakeArgRst__();
  205. this.fakeArgFn = this.__getFun__(arguments);
  206. return this.exports;
  207. };
  208.  
  209. /*
  210. * 在原方法后,劫持返回的数据
  211. *
  212. * fakeRst('直接替换为要传入的参数')
  213. * fakeRst(function(原返回值){
  214. * //
  215. * return 修改后的返回值
  216. * })
  217. */
  218. hook.prototype.fakeRst = function (arg) {
  219. this.__fakeArgRst__();
  220. this.fakeRstFn = this.__getFun__(arg);
  221. return this.exports;
  222. };
  223.  
  224.  
  225. /*
  226. * 开启劫持arg/rst
  227. */
  228. hook.prototype.__fakeArgRst__ = function () {
  229. var t = this;
  230. var fakeArgRstFn = function (args, fn_real, scope, t, raw_this) {
  231. var faked_arg = t.fakeArgFn ? t.fakeArgFn.apply(scope, args) || args : args;
  232. faked_arg && !Array.isArray(faked_arg) &&
  233. (!faked_arg.hasOwnProperty('callee') && !faked_arg.hasOwnProperty('length')) &&
  234. (faked_arg = [faked_arg]);
  235. var real_rst = t.fn_real.apply(scope, faked_arg);
  236. var faked_rst = t.fakeRstFn ? t.fakeRstFn.call(scope, real_rst) : real_rst;
  237. return faked_rst;
  238. };
  239. this.fake(fakeArgRstFn);
  240. };
  241.  
  242. /*
  243. * 关闭劫持
  244. *
  245. * 传入参数为空:关闭前后所有劫持 hook(alert).off()
  246. * 传入字符串 "arg" 或 "rst":关闭对应劫持 hook(alert).off('arg')
  247. * 传入方法:关闭对应劫持
  248. *
  249. * 前后劫持全部关闭后,还原被 hook 的方法
  250. */
  251. hook.prototype.off = function (filter) {
  252. if (!filter) {
  253. delete this.fakeArgFn;
  254. delete this.fakeRstFn;
  255. } else if (typeof filter === 'function' || filter.__proto__ === String.prototype) {
  256. (this.fakeArgFn === fn || filter === 'arg') && delete this.fakeArgFn;
  257. (this.fakeRstFn === fn || filter === 'rst') && delete this.fakeRstFn;
  258. }
  259.  
  260. if (!this.fakeArgFn && !this.fakeRstFn) {
  261. this.fn_object[this.fn_name] = this.fn_real;
  262. //delete this.storage[this.fn_object_name][this.fn_name];
  263. }
  264.  
  265. return this.exports;
  266. };
  267.  
  268. /*
  269. * 关闭前面的参数劫持
  270. *
  271. */
  272. hook.prototype.offArg = function (filter) {
  273. filter = filter || 'arg';
  274. this.off(filter);
  275. return this.exports;
  276. };
  277.  
  278. /*
  279. * 关闭后面的结果劫持
  280. *
  281. */
  282. hook.prototype.offRst = function (filter) {
  283. filter || 'rst';
  284. this.off(filter);
  285. return this.exports;
  286. };
  287.  
  288.  
  289. /*
  290. * 直接修改参数或返回结果
  291. */
  292. hook.prototype.__getcloser__ = function (args) {
  293. return function () {
  294. return args;
  295. };
  296. };
  297. hook.prototype.__getFun__ = function (arg) {
  298. return typeof arg[0] == 'function' ? arg[0] : this.__getcloser__(arg);
  299. };
  300.  
  301. return hook;
  302.  
  303. });
  304.  
  305. //检测上下文环境是否为AMD或CMD
  306. var hasDefine = typeof define === 'function',
  307. // 检测上下文环境是否为Node
  308. hasExports = typeof module !== 'undefined' && typeof module !== 'function' && module.exports;
  309. if (!name) {
  310. return definition();
  311. } else if (ctx) {
  312. // 设置环境后则挂载到此对象
  313. /*
  314. void function(name, ctx){
  315. ...
  316. }('hook', window);
  317. */
  318. ctx[name] = definition();
  319. } else if (hasDefine) {
  320. //AMD环境或CMD环境
  321. define(name, definition);
  322. } else if (hasExports) {
  323. //定义为普通Node模块
  324. module.exports = definition();
  325. }
  326. return definition();
  327.  
  328.  
  329.  
  330. // 效果演示
  331.  
  332. /*
  333.  
  334.  
  335.  
  336. window.tool = {
  337. say: '一个计算器开始工作了:',
  338. calc: function (msg, n) {
  339. console.log(this.say);
  340. console.warn('calc收到参数:' + msg + ', ' + n);
  341. var r = n * n;
  342. console.warn('calc结果:' + r);
  343. return r;
  344. }
  345. }
  346.  
  347. var hook = window.js_hook_js;
  348.  
  349. console.clear();
  350. console.info('一个计算器:');
  351.  
  352. console.group('原始方法:\n\ntool.calc');
  353. console.log(tool.calc);
  354. console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42);
  355. console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42));
  356. console.groupEnd();
  357. console.log('\n');
  358.  
  359. console.group("劫持后:\n\nhook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst('<(ˉ^ˉ)> 告诉你坏了');");
  360. hook('window.tool.calc').fakeArg('这个计算器坏了', -1).fakeRst('<(ˉ^ˉ)> 告诉你坏了');
  361. console.log(tool.calc);
  362. console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42);
  363. console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42));
  364. console.groupEnd();
  365. console.log('\n');
  366.  
  367. console.group("还原后:\n\nhook('window.tool.calc').off();");
  368. hook('window.tool.calc').off();
  369. console.log(tool.calc);
  370. console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42);
  371. console.info('接收到的结果:' + tool.calc('专注于计算平方的计算器', 42));
  372. console.groupEnd();
  373. console.log('\n');
  374.  
  375. console.group("替换:\n\nhook('window.tool.calc').fake(function(){...})");
  376. hook('window.tool.calc').fake(function (args, fn_real, scope, h, raw_scope) {
  377. console.log('调用者:' + args.callee.caller);
  378. console.log('this:');
  379. console.log(this);
  380. console.log('args:');
  381. console.log(args);
  382. console.log('fn_real:');
  383. console.log(fn_real);
  384. console.log('scope:');
  385. console.log(scope);
  386. console.log('h:');
  387. console.log(h);
  388. console.log('raw_scope:');
  389. console.log(raw_scope);
  390. return fn_real.apply({
  391. say: '怎么还是那个计算器:'
  392. }, args);
  393. });
  394. console.log(tool.calc);
  395. console.info('设置参数:' + '专注于计算平方的计算器' + ', ' + 42);
  396. console.info('接收到的结果:' + (function calc_caller() {
  397. return tool.calc.call({
  398. say: '换个计算器吧'
  399. }, '专注于计算平方的计算器', 42);
  400. })());
  401. console.groupEnd();
  402. console.log('\n');
  403.  
  404.  
  405.  
  406. */
  407.  
  408. })('js_hook_js', window);