A Weird Imagination

Working around a broken ad-block block

The problem#

Slashdot recently made a change to their ad code that made the site completely fail to load for me, showing the message

Failed to load website properly since html-load.com is blocked. Please allow html-load.com

and then blaming ad blocking:

This page could not be loaded properly due to incorrect / bad filtering rule(s) of adblockers in use. Please disable all adblockers to continue using the website. (click OK if you'd like to learn more)

I could see the page loaded just fine behind those messages, so it was obviously a lie. But the page continually reloaded if I tried to dismiss those pop-ups, so the site was unusable.

(Actually, this appears to have been disabled in the time it took me to write this blog post, so I guess this is no longer needed. Although it may be applicable to other websites using the same or similar mechanisms.)

The solution#

Install this user script. I have only tested it with Greasemonkey on Firefox so it require modification to work on other user script managers or browsers.

The details#

Disabling blocking#

I did first try to see if I could disable enough of my ad-blocking to make the script happy. Nothing I changed seemed to affect it, so I gave up on that approach quickly.

What is going on?#

I found this Slashdot post (uh, using a different browser so it would actually load) which explained the error message is part of something called Ad-Shield which attempts to detect ad blockers and intentionally break websites if they are enabled in attempt to force users to disable their ad blockers.

The comment also recommended using the tinyShield user script as a workaround. It didn't seem to have any effect for me, even after I added Slashdot to its list of URLs to apply on.

Intercepting calls#

A little while ago, I had written a user script that involved modifying fetch() and blogged about it. So I wondered if I could do something based on that code, perhaps to fake a successful response. The one thing that did give me immediately was a breakpoint in the relevant code, since it was intentionally loaded in weird way to make it difficult to inspect except by debugging through it. But none of my attempts to modify the behavior of fetch() had any effect. I tried changing its return value, making it throw an exception, and having it return a Promise that would never complete. But since the code was setup to actually require a good response from fetch() not just not get a bad response, those did not actually work.

It occurred to me that I didn't need to trick the script into thinking it was successful, I just had to stop it from showing the dialog box and performing the redirect. So instead of intercepting fetch(), I intercepted alert(), and just had it throw an exception instead of displaying a dialog box, cutting off the execution of the rest of the code. Since there's no good usages of alert() on that page, there was no need to inspect the arguments to attempt to only throw the exception for the bad usages:

const w = window.wrappedJSObject || window;
w.eval(`window.alert = ${() => {
  throw Error("Used alert().");
}}`);

As the second dialog was generated after the first one and the redirect happened after that, by throwing an exception, none of that code ran. That got the page to the point it would actually load, but I wasn't done yet.

Fixing the styles#

When the dialogs displayed, the page was visible corrected loaded behind them. But with the associated redirect disabled, after loading, it would quickly change to just plain black text on a white background like there was no CSS. And halting the script at the alert() was insufficient to prevent that change. After a lot of slowly stepping through the code in the debugger, I saw that it had a setInterval() set up to search for all styles with document.querySelectorAll("link,style") and delete them. That is, it intentionally sabotages the styles if the attempt to verify that there is no ad-blocker running fails.

The workaround is straightforward: there's no good reason to enumerate all of the styles (and this script only needs to work on this page anyway), so just return nonsense when asked to enumerate exactly "link,style":

w.eval("document.originalQuerySelectorAll = "
        + "document.querySelectorAll")
w.eval(`document.querySelectorAll = ${(arg) => {
  if (arg === "link,style") {
    return document.originalQuerySelectorAll("invalid");
  }
  return document.originalQuerySelectorAll(arg);
}}`);

Then instead of deleting the styles, it would just delete the empty list of the zero nodes matching invalid.

Page scripts broken#

Unfortunately, while that did make the page load and look correct, to be usable, the page does require JavaScript. Lower scored comments default to being folded and unfolding them is implemented using JavaScript. But that script wasn't working. In the debugger, the error was

Error: Permission denied to access object

which I had run into before as that's what you get when mismatching user script and page objects. But my script wasn't doing that. The only interaction with page objects it had was calling window.eval(), which means all of the actual code should have already been page objects.

To figure out the context of the error, I looked at the call stack of the error and set breakpoints on the code just before so I could step through and see what was going on. I eventually figured out that it was erroring on properties of the window object, but not ones that I was even modifying, which really didn't make sense.

After much confusion and staring at the debugger, I finally realized that I did have another user script running: I had never disabled the tinyShield script I had modified to add Slashdot to the list of sites it should run on. After disabling that script, the site finally worked.

Site fixed#

As I tried to recreate the issues while composing this blog post, I noticed that the Ad-Shield code was gone: the page once again works for me with the script disabled. Presumably I wasn't the only one to have issues with it and they decided it wasn't worth the trouble.

This is also a good reminder that I should be careful to take good notes, in case I have trouble reconstructing the issues that I want to write about.

Comments

Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.