0

I've got this kind of script I need to inject into

! function(e) {
    function doSomething()
    {
    }
}

Basically I get a reference to doSomething, when my code is called via Function object, but I need to hook to doSomething, so I need an original reference to id. Since doSomething is declared inside anonymous function I can't get to it. Question is, can I somehow inject code into the scope of anonymous function, Greesemonkey or any other tool.

user1617735
  • 451
  • 5
  • 16
  • What do you mean by "inject"? Are you trying to make a userscript/browser extension that manipulates JS on a certain page? Yes, you can do anything you want with that, but you cannot alter `doSomething` only with a reference to it. – Bergi Oct 11 '17 at 00:07
  • Yes, I'm writing a userscript and want to hook to some of the functions on the web page. For that I need to alter the original reference to a function that is inside the anonymous function scope. – user1617735 Oct 11 '17 at 12:02
  • Your best bet will be to intercept the script loading, change its source, and evaluate that. – Bergi Oct 11 '17 at 12:10
  • That sounds like a good solution. How do I do that? – user1617735 Oct 11 '17 at 12:50
  • No idea, it'll depend on the browser and exact technologies used. I only remember that [the old Opera had a `BeforeScript` event](http://www.opera.com/docs/userjs/specs/) exactly for this usage. – Bergi Oct 11 '17 at 14:32

2 Answers2

1

Javascript doesn't make it easy to get values from inside a scope.

You can declare doSomething in a broader scope:

function doSomething() {
    // ...
}
function func(e) {
    doSomething(); // This works! `func` has a reference to `doSomething`
}

doSomething(); // This also works! `doSomething` is declared in this scope.

You can also return values from an inner scope! For example:

function func(e) {
    function doSomething() {
        // ...
    }

    // Note that we are not invoking `doSomething`, we are only returning a reference to it.
    return doSomething; 
}

var doSomething = func(/* some value */);

// Now you got the reference!
doSomething();

Sometimes your outer function is already needed for returning another value:

function func(e) {
    function doSomething() { /* ... */ }
    return 'important value!!';
}

In this case we can STILL return doSomething, along with the original value:

function func(e) {
    function doSomething() { /* ... */ }
    return {
        value: 'important value',
        doSomething: doSomething
    };
}

var funcResult = func(/* some value */);
var originalValue = funcResult.value;
var doSomething = funcResult.doSomething;

// Now we have the original value, AND we have access to `doSomething`:
doSomething(); // This works
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
  • Well I need to hook to the function inside that scope, which means I still need to modify the original reference to that function, since it's called by multiple other functions inside that scope. If I override it with a global function, I'll only achieve all those function now calling mine, but I still need to make a call to the original function I need to hook to. – user1617735 Oct 11 '17 at 12:53
  • What about the other methods I listed? Simply including `doSomething` in the return value? – Gershom Maes Oct 11 '17 at 18:47
  • I don't have access to inner scope, that's the whole problem. – user1617735 Oct 11 '17 at 19:11
  • You don't have access to the source code where these functions are defined? That's very different from not having access to the scope :P – Gershom Maes Oct 11 '17 at 21:33
  • 1
    I don't have access to anything :) It's just a web site that deploys their scripts the way it's all inside one giant anonymous function. I'm trying to write a user script for it. – user1617735 Oct 12 '17 at 21:34
  • Are you sure the only way you can accomplish what you want is by accessing the unscoped script? If you post more code there may be another way to solve your problem! – Gershom Maes Oct 12 '17 at 23:16
  • 1
    I'm not sure what else I can add to the original question. The page has a script, which is wrapped in anonymous function. I want to write a user script that will add functionality to the page once it's loaded. One of the things I need to do is hook to the function inside hidden scope, for that I need an original reference to that function, so I can replace it with my own, but it's inaccessible. What else should I add to the description of the problem? – user1617735 Oct 15 '17 at 16:28
  • I'm thinking that maybe you don't need to hook into the function. – Gershom Maes Oct 15 '17 at 18:57
  • 1
    Howcome? There are functions inside that scope that use the function I want to hook to, and I need all of them to start calling my implementation. In my implementation I do what I need and then call the original function. – user1617735 Oct 15 '17 at 19:20
  • Maybe there's another way to go about it. Often there is! – Gershom Maes Oct 15 '17 at 19:23
1

Hey man I saw you're question after doing a google search, because I was interested in this same topic, and I was a bit frustrated how other people didn't seem to understand what you were asking... but after some thought, I actually proposed a solution!

First I'll show you how to do it if you were to write a chrome extension, which would be the simplest way to guarantee we are intercepting the script source, but there is also a way to do it without making an extension, see below further.

Basically, say you have some site that you want to write an addon for, with some HTML content like:

<script src="http://www.someURL.com/someFile.js"></script>

and the file someFile.js has content like the following:

(function() {
    function cantGetMeMethod() {

    }
})();

So the user, at the main page where the HTML is being executed, is unable to get the function "cantGetMeMethod".

The thing is, if we were able to simply change the source code of that JavaScript file, we could either easily remove the anonymous function wrap, or insert some kind of global variable reference to it at the bottom.

But how can we possibly change JavaScript source code from the client side?

That's where chrome extensions come in. With extensions, its possible to redirect an HTTP request that is made anywhere on the site, before the page even loads, to another website. So, for example, if a page had an image like:

<img src="http://example.com/somePic.png"></img>

the extension can redirect all requests made from example.com to another website, so the real image that is displayed (to the client with the extension) could be actually sourced at some other website, and be an entirely different image!

How is this relevant to JavaScript?

Because the same principle could work by JavaScript sources also. All we have to do is find the JavaScript references to other files, and, with the extension, redirect the src URL to our own server, with the original src URL as a get paramter. So say we have some nodeJS site or something hosted at http://www.myAwesomeNodeJSserverOrSomething.com, so we redirect all calls (or at least the relevant script calls) made to http://www.someURL.com/someFile.js to http://www.myAwesomeNodeJSserverOrSomething.com/http://www.someURL.com/someFile.js

and then on the nodeJS side do something like:

require("http")
.createServer((q,r) => {
    http.get(q.url.slice(1), req => {
        let str = "";
        req.on("data", d => str += d.toString())
        req.on("end", () => {
            let newCode = someFunctionThatModifiesCode(str);
            r.end(newCode);
        });
    }); //pseudocode obviously, check for errors etc. for real
}).listen(8080);

and now the page has modified JavaScript code in it! So how do we make a quick extension that redirects the headers? First, make a new directory, and make in it a file called manifest.json, like this:

{
    "name":"JavaScript cracking the codes",
    "version":"1.0",
    "description":"hi",
    "manifest_version":2,
    "permissions": [
        "webRequest",
        "webRequestBlocking",
        "<all_urls>"
    ],
    "background": {
        "scripts":["lol.js"],
        "persistent": true
    }
}

now make a new file "lol.js" in the same directory, and put in it something like this:

let otherServer = "http://www.myAwesomeNodeJSserverOrSomething.com",
    urlsToRedirect = [ //list of javascript files that need fine tuning
        "http://www.someURL.com/someFile.js",
        "http://www.someURL.com/someFile2.js",
        "http://www.someURL.com/someFile3.js",
    ];
chrome.webRequest.onBeforeRequest.addListener(
    details => {
        if(
            urlsToRedirect.includes(
                details
                .url
            )
        ) {
            {
                redirectUrl: (
                    otherServer 
                    + "/"
                    + details.url
                )
            }
        }
    },
    {urls: ["<all_urls>"]},
    ["blocking"]
);

(warning: untested code)

And go to chrome settings, put in in developer mode, and load in a new extension, and select that folder.

Also, the way to do it without an extension, if you yourself were just doing it, would be to inject a mutationobserver script before the page loads, with somethinng like tamper monkey. For more on this see https://github.com/CertainPerformance/Stack-Exchange-Userscripts/blob/master/obsolete/Experiment-Off/StackExperimentOff.user.js https://stackoverflow.com/a/59424277/2016831 Let me know if that solves it for you.