拦截javascript中的函数调用

Posted

技术标签:

【中文标题】拦截javascript中的函数调用【英文标题】:Intercepting function calls in javascript 【发布时间】:2018-03-06 08:39:20 【问题描述】:

如果有人能告诉我如何拦截 javascript 中的函数调用,我将不胜感激。 我知道使用代理是可能的。

例如我尝试了下面的代码来拦截它,但现在我想拦截toDataURL()。为了调用 toDataURL,您需要先创建一个画布元素。所以,现在我想知道如何定义一个代理来拦截toDataURL()

拦截它的示例代码:

window.x = 0;
    let calls = (function()
        let canvas = document.createElement('canvas');
        let fun = canvas.toDataURL;
        canvas.toDataURL = function()       
            window.x++;         
            return fun.apply(document, arguments);
        
        return ()=>calls;
    )();

【问题讨论】:

在我看来,您已经修改了canvas.toDataURL,而不是如您所说的document.createElement。我只是想知道 return ()=>calls; 在 IIFE 中是干什么用的。除此之外,这种方法也不是通用的;它修改了创建元素的方法。可以尝试修改htmlCanvasElement.prototype.toDataURL 您好,我尝试了上面的代码,但没有成功。可以举个例子吗? 尝试事件,比拦截更简单更快 @Bathooman ...现在有两种方法...一种基于方法修改/功能组合,另一种基于Proxy。每个都通过工作示例代码证明自己。 【参考方案1】:

尽管修改标准类型的标准方法总是有充分的理由,但 JS 中方法修改的基本方法是包装。甚至可以考虑标准化Function.prototype[before|after|around|afterThrowing|afterFinally]。然后修改任何给定的方法或函数将与下一个提供的示例一样简单,这也可能是 OP 正在寻找的答案...

(function (Function) 
  var
    isFunction = function (type) 
      return (
           (typeof type == "function")
        && (typeof type.call == "function")
        && (typeof type.apply == "function")
      );
    ,
    getSanitizedTarget = function (target) 
      return ((target != null) && target) || null;
    ;

  Function.prototype.before = function (handler, target)  // before
    target = getSanitizedTarget(target);
    var proceed = this ;

    return (isFunction(handler) && isFunction(proceed) && function () 
      var args = arguments;

      handler.call((target || this), args);
      return proceed.apply((target || this), args);

    ) || proceed;
  ;
  Function.prototype.after = function (handler, target)  // afterReturning
    target = getSanitizedTarget(target);
    var proceed = this ;

    return (isFunction(handler) && isFunction(proceed) && function () 
      var ret, args = arguments;

      ret = proceed.apply((target || this), args);
      handler.call((target || this), ret, args);

      return ret;

    ) || proceed;
  ;
  Function.prototype.around = function (handler, target)  // around
    target = getSanitizedTarget(target);

    var proceed = this ;
    return (isFunction(handler) && isFunction(proceed) && function () 

      return handler.call((target || this), proceed, handler, arguments);

    ) || proceed;
  ;
(Function));


function modifyCanvasToDataUrlAfter(returnValue, thisArgs) 
  console.log('modifyCanvasToDataUrlAfter :: thisArgs : ', thisArgs);
  console.log('modifyCanvasToDataUrlAfter :: returnValue : ', returnValue);

HTMLCanvasElement.prototype.toDataURL = HTMLCanvasElement.prototype.toDataURL.after(modifyCanvasToDataUrlAfter);


var elmToJpgLow = document.getElementById('canvasToLowResolutionJpg');
var elmToJpgMedium = document.getElementById('canvasToMediumResolutionJpg');

console.log("elmToJpgLow.toDataURL('image/jpeg', 0.1) : ", elmToJpgLow.toDataURL('image/jpeg', 0.1));
console.log('elmToJpgLow.toDataURL : ', elmToJpgLow.toDataURL);

console.log("elmToJpgMedium.toDataURL('image/jpeg', 0.1) : ", elmToJpgMedium.toDataURL('image/jpeg', 0.5));
console.log('elmToJpgMedium.toDataURL : ', elmToJpgMedium.toDataURL);
.as-console-wrapper  max-height: 100%!important; top: 0; 
<canvas id="canvasToLowResolutionJpg"  ></canvas>
<canvas id="canvasToMediumResolutionJpg"  ></canvas>

编辑

基于Proxy 的方法可能类似于以下提供的示例...

const canvasToDataUrlModifier = 
  apply: function(target, thisArg, argumentsList) 

    let returnValue = target.apply(thisArg, argumentsList);

    console.log('toDataUrlAfterModifier :: argumentsList : ', argumentsList);
    console.log('toDataUrlAfterModifier :: returnValue : ', returnValue);

    return returnValue;
  
;

var elmToJpgLow = document.getElementById('canvasToLowResolutionJpg');
var elmToJpgMedium = document.getElementById('canvasToMediumResolutionJpg');

var proxyToJpgLow = new Proxy(elmToJpgLow.toDataURL, canvasToDataUrlModifier);
var proxyToJpgMedium = new Proxy(elmToJpgMedium.toDataURL, canvasToDataUrlModifier);

console.log("proxyToJpgLow.call(elmToJpgLow, 'image/jpeg', 0.1) : ", proxyToJpgLow.call(elmToJpgLow, 'image/jpeg', 0.1));

console.log("proxyToJpgMedium.call(elmToJpgMedium, 'image/jpeg', 0.5) : ", proxyToJpgMedium.call(elmToJpgMedium, 'image/jpeg', 0.5));
.as-console-wrapper  max-height: 100%!important; top: 0; 
<canvas id="canvasToLowResolutionJpg"  ></canvas>
<canvas id="canvasToMediumResolutionJpg"  ></canvas>

【讨论】:

感谢您的回复,彼得,您知道我实际上要做的是在调用canvas.toDataURL() 时引起注意。看来我无法使用您建议的方式来实现这一点。我已经使用下面的代码为document.createElement 做了这个:let calls = (function() let fun = document.createElement; document.createElement = function() window.doc.push(arguments); return fun.apply(document, arguments); return ()=&gt;calls; )(); @Bathooman ...不客气。请描述一下,这两种方法中的什么都不适合您。所有给定的示例都提供了运行代码。因此,人们应该能够更容易地理解每种方法是如何运作的。另一方面,我没有得到 Q 中提供的代码(请参阅我的评论)以及您上面评论中的代码打算实现的目标。我不知道你期望它做什么以及它在哪里、为什么以及它失败的错误消息。提供更大的图景会更有帮助。 亲爱的彼得,我只想在调用 canvas.toDataURL() 时得到通知。我真的不知道该怎么办。请帮忙!! document.createElement 可以被覆盖:var origParseFloat = parseFloat; parseFloat = function(str) alert("And I'm in your floats!"); return origParseFloat(str); @Bathooman ...请首先回复 3 月 7 日 11:08 的评论...只要不知道代码的哪一部分调用,任何进一步的回答尝试都只是猜测canvas.toDataURL() 以及必须拦截或需要通知的部分。我在 3 月 6 日 10:38 的回答提供了两种不同的方法,每种方法都提供了工作示例代码。你尝试了什么?你在哪里卡住了?

以上是关于拦截javascript中的函数调用的主要内容,如果未能解决你的问题,请参考以下文章

使用LD_PRELOAD拦截共享函数库的函数调用

Android:拦截来自WebView的AJAX调用

js打开新窗口并且不被拦截

Android 逆向函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )

拦截 bash 脚本函数/系统调用并将它们包装到自定义函数中

将 iOS 中被调用函数的结果返回给 JavaScript 中 WkWebView 中的调用函数