script request/response hook

用来给script类型的请求打断点

目前為 2021-01-02 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name script request/response hook
  3. // @namespace https://github.com/CC11001100/crawler-js-hook-framework-public
  4. // @version 0.2
  5. // @description 用来给script类型的请求打断点
  6. // @author CC11001100
  7. // @match *://*/*
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (() => {
  13.  
  14. /**
  15. * 使用说明:
  16. * 用来hook通过script发请求和服务器交互的(多为jsonp),这种打xhr断点没用
  17. *
  18. * 使用方式:
  19. * urlContainsHook: 用来在请求发送之前hook,请求的url中随便复制一点可标识的部分粘贴在这里即可,
  20. * 只要检测到script发出的请求的url包含给定的字符串就会hook住进入断点,
  21. * 使用场景是url中有加密参数的话就可以往前追溯定位参数怎么来的
  22. *
  23. * debuggerBeforeRequestSend: 是否在发送请求前进入断点,如果只需要打response断点的话则手动置为false即可
  24. *
  25. * jsonpCallbackFunctionNameParam: 注意服务器的响应内容,如果服务器是返回的callback({foo: bar})形式的,
  26. * 就将callback函数的名字粘贴到这里,适用场景是jsonp回调函数的名字不变
  27. *
  28. * jsonpCallbackFunctionNameParam: 用来在接收到服务器响应时进入断点,如果是以jsonp形式交互的,则服务器返回的是一个函数调用的形式,
  29. * 具体调用哪个函数是请求参数传递的,这里指定的就是这个参数的名字,会把函数名字替掉添加response断点,
  30. * 用于jsonp回调函数每次都会变的,典型的就是jsonp函数名称总有时间戳
  31. * 使用场景是如果响应体是加密的可以跟进去看解密逻辑
  32. *
  33. */
  34.  
  35. const urlContainsHook = "localhost";
  36.  
  37. // 如果为true则在请求发送前进入断点,否则请求发送前不进入断点
  38. // 如果请求参数比较明确,但是返回内容是加密的,这个时候只关注响应是如何处理的,则可以将这个选项置为false
  39. const debuggerBeforeRequestSend = true;
  40.  
  41. // 如果jsonp的回调不是通过参数传递的,而是服务端返回时写死的,看一眼函数的名字,配置在这里,会把这个函数替换掉以插入断点
  42. const jsonpCallbackFunctionName = "";
  43.  
  44. // 如果每次都会变,则只能从参数中取名字
  45. const jsonpCallbackFunctionNameParam = "";
  46.  
  47. const createElementHolder = document.createElement;
  48. document.createElement = function () {
  49. const result = createElementHolder.apply(this, arguments);
  50. if (arguments.length && arguments[0].toLowerCase() === "script") {
  51. // 在设置src时拦截,然后就可以去追溯src是怎么来的了
  52. Object.defineProperty(result, "src", {
  53. set: newValue => {
  54.  
  55. if (!urlContainsHook || (newValue.indexOf(urlContainsHook) !== -1)) {
  56.  
  57. // 请求时进入断点
  58. if (debuggerBeforeRequestSend) {
  59. debugger;
  60. }
  61.  
  62. // jsonp函数是服务器返回固定写死的hook
  63. if (jsonpCallbackFunctionName) {
  64. hookFunctionByName(jsonpCallbackFunctionName);
  65. }
  66.  
  67. // jsonp的函数名称是每次都不同一直在变的
  68. if (jsonpCallbackFunctionNameParam) {
  69. const oldJsonFuncName = new URL(newValue).searchParams.get(jsonpCallbackFunctionNameParam);
  70. hookFunctionByName(oldJsonFuncName);
  71. }
  72.  
  73. }
  74. delete result.src;
  75. result.src = newValue;
  76. },
  77. configurable: true
  78. });
  79. }
  80. return result;
  81. }
  82.  
  83. function hookFunctionByName(functionName) {
  84. // 因为是要在新的script中调用,所以这些jsonp函数都是全局作用域
  85. if (!window[functionName]) {
  86. console.log(`hook失败,函数不存在: ${functionName}`);
  87. return;
  88. }
  89.  
  90. // 如果已经Hook过了则不重复hook
  91. const hookDoneFlag = "hookDoneFlag";
  92. if (window[functionName][hookDoneFlag]) {
  93. return;
  94. }
  95. const holder = window[functionName];
  96. window[functionName] = () => {
  97. // 这里是脚本的响应断点,已经拦截到响应,跟进去holder函数就行了
  98. debugger;
  99. holder(...arguments);
  100. }
  101. window[functionName][hookDoneFlag] = true;
  102. }
  103.  
  104. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址