[ js.hook.js ]

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

目前为 2016-10-31 提交的版本。查看 最新版本

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