Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Can I get function value without toString()?

I need to make sure that some specific native Javascript functions are not patched nor overrode.

Unfortunately, I cannot do that with accessing the .toString() of the function or Function.prototype.toString with one of bind apply or call, since the Function.prototype.toString is one of the functions I have to test.

Is there any other method which returns the value (the function itself) of a function? (Or [Native Code] for native JS functions)

Editing: One of the purposes of this test is to check if the client is a bot that patches some JS functions. Creating new frame and taking its Function.prototype.toString value won't work in that case

like image 639
GMe Avatar asked Nov 06 '22 09:11

GMe


1 Answers

In response to edit

If it's a malicious client that can't or won't update their bot script in response to your checks, then just save a copy of Function.prototype.toString() using javascript in your HTML header to a temp variable. Then check against this to see if the client has mutated the js at all.

If the client is malicious AND is trying to actively avoid your checks by changing their bot, then there is simply no iron-clad way to stop them. It will become an arms-race where you patch in checks and they patch in fixes in response. Ultimately, the client has the final say in what gets run in their browser, so you might want to think again about why you're doing these checks to see if there's another viable approach to your problem.

Initial Answer

You could re-request the whole .js file and parse it all as a string. You would have to do this for every js file and find a good pattern for determining if your functions have been over-written, so it may not work for your needs.

// my JS file is being served from giorgiosjames.com/myjs.js

const myJs = await fetch('giorgiosjames.com/myjs.js').then(res => res.text());

// parse myJs here, something like
if (myJs.includes('Function.prototype.toString = ')) // do something

If you can constrain your use to the latest Firefox, you can use the method .toSource(), but no other browsers support it and it is not standard. More reading here

// only in latest Firefox
const x = () => 'wow';
console.log(x.toSource())
// returns "() => 'wow'"

And as a frame challenge, you could probably still be using the (arguably) best approach of Function.prototype.toString by:

  1. First checking if toString works.

  2. Resetting .toString() if it has been overridden.

  3. Checking your other functions with .toString()
  4. Un-fixing .toString() if necessary
let temp = null;

if (Function.prototype.toString.toString() !== 'function toString() { [native code] }') {
    // save overridden function if you need to come back to it
    temp = Function.prototype.toString;

    // getting the original toString function by creating an iframe
    const iframe = docuemnt.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    Function.prototype.toString = iframe.contentWindow.Function.prototype.toString;
}

// do your other checks here ex//
if (Array.prototype.includes.toString() !== iframe.contentWindow.Array.prototype.includes.toString()) {
    // do something
}

// ..or however you're planning on checking for overridden functions

// restore toString from temp if needed.
Function.prototype.toString = temp;
like image 109
GiorgiosJames Avatar answered Nov 14 '22 00:11

GiorgiosJames