0

I'm trying to override Object() calls in JavaScript, in order to intercept and save all the parameters passed to the object constructor.

I don't have any access to the original code since I am running from an injected code but I do run from the same scope and share the same window of the original code.

I tried this approach:

(function (nativeObject) {
    window.Object = function (value) {
        if (value.magic) {
            window.myMagic = value.magic;
        }
        return nativeObject(value);
    }
})(Object);

as well as saving the original window.Object on window.nativeObject and calling it from within my hook, but in both ways I end up getting:

TypeError: Object.defineProperty is not a function
TypeError: Object.keys is not a function
Uncaught (in promise) TypeError: Object.isExtensible is not a function
Uncaught (in promise) TypeError: Object.create is not a function

Is it because my window.myMagic calls the the Object methods to set the myMagic key in the window object?

Is what I'm trying to do possible?

amiregelz
  • 1,833
  • 7
  • 25
  • 46
  • 1
    In what code do you need to intercept `Object` calls? That sounds like a really bad idea. – Bergi Dec 26 '21 at 21:40
  • @Bergi I'm trying to get statistics about data being transferred in a certain web client (and don't have the access to the code). I want to set hooks on whatever built in functions I can, like `XMLHttpRequest`, `Object`, etc - and do some analytics based on it. Is there a smarter way to do this? – amiregelz Dec 26 '21 at 21:46
  • Transferred from where to where? But no, intercepting `Object` calls is unlikely to be effective, since most objects are constructed through other means. – Bergi Dec 26 '21 at 22:05
  • @Bergi And is there any way to hook and intercept object assignments like `myObject.key = ...`? – amiregelz Dec 26 '21 at 22:19
  • It's possible with getters (and, if you don't know the `key` name, with proxies), but for both you must have control over `myObject` and it only works with assignments. Still not sure what your goal (or even concrete problem) is, so I can't give practical advice. – Bergi Dec 26 '21 at 22:34

2 Answers2

1

You are replacing Object with a function that doesn't have any of the expected static methods on it. Calling Object.defineProperties no longer works, it's nativeObject.defineProperties that would be required.

You can however easily copy those properties from the native function onto your replacement function, to avoid breaking code that relies on them:

(function (nativeObject) {
    window.Object = function (value) {
        if (value.magic) {
            window.myMagic = value.magic;
        }
        return nativeObject(value);
    }
    for (const key of Reflect.ownKeys(nativeObject)) { // names and symbols
        const descriptor = nativeObject.getOwnPropertyDescriptor(nativeObject, key);
        nativeObject.defineProperty(Object, key, descriptor);
    }
})(Object);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I'm getting `nativeObject.defineOwnProperty is not a function`. Also, looking at [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) and [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy) got me thinking, is there a better or easier to create a proxy like that? – amiregelz Dec 26 '21 at 21:53
  • 1
    @amiregelz Ooops, now updated with tested code :-) No, a `Proxy` won't really help here. – Bergi Dec 26 '21 at 22:06
  • How does this solution differ from GOTO 0's? – amiregelz Mar 26 '22 at 18:05
  • @amiregelz Apart from the obvious (not using an unnecessary proxy), the approach also works in an ES5 environment (ok, you'll have to use `Object.getOwnPropertyNames` instead of `Reflect.ownKeys` of course). Is there something specific you want to know about? – Bergi Mar 26 '22 at 18:39
1

I'm going to show how to replace the Object constructor with a proxy: we need to implement both the apply and construct traps for invocations of Object without or with new.

(nativeObject => {
    function Object(value) {
        if (value.magic) {
            globalThis.myMagic = value.magic;
        }
        return nativeObject(value);
    }
    globalThis.Object = new Proxy(nativeObject, {
        apply: (target, thisArg, args) => Object(...args),
        construct: (target, args) => Object(...args)
    });
})(Object);

// Test
new Object({ magic: 42 });
console.log(myMagic); // prints 42
Object({ magic: 'foo' });
console.log(myMagic); // prints "foo"
console.log(Object.getOwnPropertyNames(Object)); // prints an array of property names

Just note that this method will not work with any predefined functions, which may require different treatments for apply and construct. The Object function is a special case in that it behaves in the same way with or without new.

GOTO 0
  • 42,323
  • 22
  • 125
  • 158
  • Can I use the same approach to proxy and hook [function calls](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)? – amiregelz Dec 29 '21 at 16:24
  • @amiregelz Sort of. It would take a couple of changes to make it work. Do you have a particluar example in mind? – GOTO 0 Dec 29 '21 at 20:09
  • Yes :) I want to detect when a specific function is being called by intercepting all function calls and looking at the called function signature and its string representation. – amiregelz Dec 29 '21 at 23:29
  • @amiregelz The difficult part is not [creating the proxy](https://jsfiddle.net/z542jeya/), but replacing the original function, and this can be done only when the the function to be intercepted is accessible at the time of proxying. – GOTO 0 Dec 30 '21 at 08:17
  • Got it. So it's not possible to proxy the `Function` built-in method/constructor to intercept all functions simultaneously? – amiregelz Dec 30 '21 at 17:21
  • @amiregelz nope... – GOTO 0 Dec 30 '21 at 23:46
  • It looks like it works perfectly, however I see there is some function call (in the website code which is not mine) that fails once I set this hook, and succeeds if I remove it. The error is not very clear (`every is not a function`) - but regardless - is there any chance this hook could have bad effect on some function calls? – amiregelz Mar 26 '22 at 18:05
  • @amiregelz It could have unexpected effects, sure. – GOTO 0 Mar 26 '22 at 21:11
  • Is there anything I can do to prevent that? aren't the parameters passed exactly the same as they would originally? what about the `this` arg? – amiregelz Mar 27 '22 at 01:05
  • @amiregelz `this` and the parameters are the same, but there are other differences. I would start with debugging the error you observe to find out where in your code the difference is arising. – GOTO 0 Mar 27 '22 at 07:51
  • I can't put my fingers on what's special about the code that fails, it's just a call to S3 addAuthorization (when uploading a file), which looks like a normal function call. Is it possible to cancel the Proxy once I find my `magic`? maybe that will help avoid messing things up. – amiregelz Mar 27 '22 at 08:06
  • @amiregelz Certainly. Just save the original value of `Object` in a variable before installing the proxy, then when you are done with it, reset the original value with `globalThis.Object = originalObject;`. – GOTO 0 Mar 27 '22 at 08:48