Tag: Hacking

Block Comments by EricJ on mst3kinfo.com

If you're a longtime reader of Satellite News (mst3kinfo.com) like me, you know that it's got a pretty good comments section, except for two things:

  1. A particularly obnoxious and persistent troll by the name of EricJ who insists on pissing in everyone's cornflakes; and
  2. A bunch of other posters with poor self-control who insist on responding to him.

Awhile ago the site admins updated their software to allow you to block users. That takes care of #1. But unfortunately, blocking a troll isn't much use when people keep replying to him and quoting his posts.

And so, in the tradition of my Hide Techdirt Comments script, I've written a userscript that will block EricJ and replies that quote him. Works with Greasemonkey, Tampermonkey, and presumably any other similar userscript plugins that may be out there.

If there's anybody else who bothers you, you can add other usernames to the blacklistedUsers array, too.

And ordinarily, I wouldn't even name the troll I was talking about, because the entire point here is that you shouldn't give trolls the attention they crave -- but I figure you know, this post might prove useful to other Satellite News commenters, so I should probably put his name in it so that maybe somebody will find it while searching for a way to block all comments from, and replies to, The Original EricJ on mst3kinfo.com.

Enjoy.

// ==UserScript==
// @name          Hide Satellite News Comments
// @namespace     http://corporate-sellout.com
// @description	  Hide comments on mst3kinfo.com, based on user
// @include       http://www.mst3kinfo.com/?p=*
// @require       http://www.mst3kinfo.com/wp-includes/js/jquery/jquery.js
// ==/UserScript==

// List of users whose comments you want to hide --
// you can add more names to this list, but let's be honest, you want to block EricJ.
const blacklistedUsers = [
  'The Original EricJ'
];

const $ = jQuery;

// Comment class
// Constructor
function Comment(node) {
  this.node = node;
  this.nameBlock = $('.comment-author > .fn > a', this.node);
  this.name = this.nameBlock.text();
  this.quotedUserBlock = $('a[href^="#comment"]', this.node);
  
  this.quotedUser = this.quotedUserBlock.length === 1
    ? this.quotedUserBlock.text()
    : '';
}

// Functions
Comment.prototype = {
  constructor: Comment,
  
  check: function() {
    if(
      blacklistedUsers.includes(this.name)
      || (this.quotedUser !== '' && blacklistedUsers.includes(this.quotedUser))
    ) {
      this.node.remove();
      return true;
    }
    return false;
  }
};

$('.comment').each(function() {
  const cmt = new Comment($(this));
  cmt.check();
});

License

I'm not a lawyer, but my opinion as a programmer is that this script is too short, simple, and obvious to be copyrightable. As such, I claim no copyright, and offer no license, because none is needed. Use it however you want, with the standard disclaimer that it comes with absolutely no warranty.

Hide Techdirt Comments

Updated 2019-04-11: General cleanup; change to OOP.

Remove some techniques that are no longer needed since recent Techdirt update; add handling for some new types of predictable troll behavior.

Better blocking of flagged users who aren't logged in.


Updated 2018-08-19: Hide comments that have already been hidden by user flagging (this is mostly useful if the hideReplies boolean is set true).


Updated 2018-08-15: Added hideLoggedOut. If set true, then the script will hide any user who isn't logged in, unless their name is in the whitelist array.

Added hideReplies. If set true, then when the script hides a comment it will also hide all the replies to the comment.

If you set both hideLoggedOut and hideReplies to true, then the Techdirt comments section gets much quieter.


Updated 2018-08-09: Some doofus has been impersonating me. Script will now automatically flag and hide posts by fake Thad.

In addition to hiding posts if their subject line is too long, the script will now also hide posts if the username is too long. Additionally, the script can automatically flag posts if the subject or username exceeds a specified length.

This thing's gotten complicated enough that I think it's probably subject to copyright now. I've added a license. I chose a 3-Clause BSD License.


Updated 2018-06-20: Ignore mixed-case and non-alpha characters.


Updated 2018-03-06: Fixed case where usernames inside links were not being blocked.


Updated 2018-03-04: Added function to hide long subject lines, because some trolls like to write manifesto-length gibberish in the Subject: line.

There is now a maxSubjectLength variable (default value: 50). Any subject line exceeding that length will be hidden. If you reply to a post with a subject line exceeding that length, your reply's subject line will default to "Re: tl;dr".


Updated 2017-07-12: Added @include.


In my previous post, I mentioned that I spend too much of my life responding to trolls on Techdirt.

With that realization, I whipped up a quick Greasemonkey/Tampermonkey script to block all posts from specified usernames.

// ==UserScript==
// @name          Hide Techdirt Comments
// @namespace     http://corporate-sellout.com
// @description	  Hide comments on Techdirt, based on user and other criteria.
// @include       https://www.techdirt.com/articles/*
// @include       https://www.techdirt.com/blog/*
// @require       https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// ==/UserScript==

// Boolean settings:
// if true, hide all posts from users who aren't logged in
const hideLoggedOut = true,

// if true, hide all replies to hidden posts
  hideReplies = true;

// List of users whose comments you want to hide -- collect 'em all!
const blacklistedUsers = [
  'MyNameHere',
  'Richard Bennett',
  'John Smith',
  'ROGS',
  'JEDIDIAH',
  'Zof',
  'Bamboo Harvester',
  'btr1701',
  'Prinny'
],

// If an anonymous post begins with one of these strings, hide it
blacklistedStrings = [
  'out_of_the_blue',
  'Nothing to hide, nothing to fear'
],

// List of users whose comments you don't want to hide
whitelistedUsers = [
  'Chip',
  'Thad'
];

// global variable for storing gravatars of non-logged-in posters who have been blocked
let blockedGravatars = [],

// global variable for storing comments that aren't hidden
comments = [];

// check all non-hidden comments for a blocked gravatar
// (check each time a gravatar is blocked)
function checkCommentsForBlockedGravatar(blockedGravatar) {
  for(let i=0; i<comments.length; i++) {
    if(comments[i].gravatar === blockedGravatar) {
      comments[i].gravatarBlocked = true;
      comments[i].removeComment();
    }
  }
}


// Comment class
// Constructor
function Comment(node) {
  this.node = node;
  this.nameBlock = $('.commentname > div', this.node);
  this.name = $('> :first-child', this.nameBlock).text();
  this.loggedIn = $('> :nth-child(2)', this.nameBlock).text() === 'profile';
  this.gravatar = $('.commentname > img', this.node).attr('src');
  this.gravatarBlocked = false;
  this.flagBtn = $('.report-button', this.node);
  this.alreadyHidden = ($('> .abusivecomment', this.node).length > 0);
  this.alreadyFlagged = this.flagBtn.hasClass('down');
  this.postBody = $('.cbody', this.node).text().trim();
  
  // If I click on the "Flag" button, remove the comment
  var that = this;
  that.flagBtn.one('click', function() {
    that.removeComment();
  });
}

// Functions
Comment.prototype = {
  constructor: Comment,
  
  checkForBlockedGravatar: function() {
    if(this.loggedIn) {
      return false;
    } else if(this.gravatarBlocked !== true) {
      // only need to find gravatar in blockedGravatars array once;
      // once this.gravatarBlocked is set true, then it will always be true.
      this.gravatarBlocked = blockedGravatars.includes(this.gravatar);
    }
    return this.gravatarBlocked;
  },
  
  blockGravatar: function() {
    this.gravatarBlocked = true;
    blockedGravatars.push(this.gravatar);
    checkCommentsForBlockedGravatar(this.gravatar);
  },
  
  hideReplies: function() {
    const next = this.node.next();
    if(next.hasClass('nest')) {
    // Comment has replies; remove them.
      next.remove();
    }
  },
  
  removeComment: function() {
    if(hideReplies === true) {
      this.hideReplies();
      this.node.remove();
    } else {
      // replace comment with 'removed'
      // -- because replies will still be visible, this is necessary
      // so you can tell there's a missing post that they're replying to.
      this.node.text('removed');
    }
    if(!this.loggedIn && !this.gravatarBlocked) {
      this.blockGravatar();
    }
  },
  
  badStart: function() {
    for(let i=0; i<blacklistedStrings.length; i++) {
      if(this.postBody.startsWith(blacklistedStrings[i])) {
        return true;
      }
    }
    return false;
  },
  
  check: function() {
    if(
      this.alreadyHidden
      || this.alreadyFlagged
      || this.checkForBlockedGravatar() === true
      || blacklistedUsers.includes(this.name)
      || (this.loggedIn === false && hideLoggedOut === true && !whitelistedUsers.includes(this.name))
      || (this.name === 'Anonymous Coward' && this.badStart())
    ) {
      this.removeComment();
      return true;
    }
    return false;
  }
};

$('.cmt').each(function() {
  // skip comment if it's already been removed
  if(document.contains($(this)[0])) {
    const cmt = new Comment($(this));
    if(cmt.check() === false) {
      comments.push(cmt);
    }
  }
});

License

Copyright 2017-2019 Thaddeus R R Boyd

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Further Thoughts

(Note: The script was much smaller when I originally wrote this part of the post.)

This is a blunt instrument; it took about five minutes to write. It lacks subtlety and nuance.

Blocking all anonymous posters on Techdirt is not an ideal solution; most anons aren't trolls. (Most trolls, however, are anons.) I apologize to all the innocent anons blocked by this script.

I could make the script more precise. Techdirt's trolls are creatures of habit with certain noticeable verbal tics (more on that below); if I had a good parser, I think I could whip up a scoring system that could recognize troll posts with a high degree of accuracy.

The question is, how much time do I want to spend on that?

On the one hand, "five minutes in a text editor" is the appropriate amount of time for dealing with forum trolls. Anything else seems like more effort and attention than they deserve.

On the other hand, it's a potentially interesting project, I've always wanted to spend some time studying natural language processing, and any programming project is time well-spent if it teaches you a new skill.

So I haven't decided yet. Here's the script as it stands, in its initial, blunt-instrument-that-took-five-minutes form. If I update the script, I'll update this post.

Chip Tips

Lastly, as I can no longer see anonymous posts, this means I will likely have to give up my beloved sockpuppet, Chip, the man who hates all government regulations and loves to eat leaded paint chips. To anyone and everyone else who wants to keep the spirit of Chip alive, you have my blessing to post under his name.

A few tips on how to write as Chip:

  • Never use the backspace key.
  • Remember to add random Capital Letters and "quotation marks" to your posts, in Places where they "don't" make Sense!
  • Most sentences should end with Exclamation Points!
  • I told you So!
  • I have "lots" of Solutions! So many I can't Name a single "one"!
  • Sycophantic Idiots!
  • Every Nation eats the Paint chips it Deserves!

Boy, my regular readers are going to have no fucking idea what I'm talking about in this post.

Come back tomorrow; I plan on having a post about online privacy that should be a little less niche.

Backing Up Wii Data -- All of It

So I've been having problems with my Wii. It's stopped running discs entirely -- I put one in, it spits it right back out. I suspect the spindle motor, and I'm going to try fixing it myself with a little help from the guides and parts at Console Zombie -- but before I go taking my Wii apart and poking around in its innards, I figure I should probably back all my shit up.

Course, as you may know, the Wii doesn't allow you to back up everything onto an SD card. Certain downloads and save files are copy-protected. This is what is known, amongst technical people such as myself, as a bunch of stupid fucking bullshit.

See, the way I see it, I should be able to back up my saves in case my console gives up the ghost. Or, say, go over to my brother-in-law's house and have access to every course on Mario Kart without having to unlock them all again in fucking single-player mode.

So I did a bit of reading up and found a utility called Savegame Extractor. It requires installation of the Homebrew Channel.

I have an old Wii and the latest version of the Wii System Menu (4.3U). After some reading, I found that the appropriate utility for my system was LetterBomb, and there are installation instructions at wiibrew.org.

It was about as simple and painless as root tools come. Select your firmware version and input your MAC address, then download the LetterBomb zipfile. Rename the private folder on your SD card, copy the boot.elf file and private directory from the zip to the root. Put it in the Wii, power it up, open up the messageboard, and click on the LetterBomb icon. From there I installed the Homebrew Channel, and installed BootMii as boot2 (apparently on recent Wii revisions you can only install as IOS, ie overwriting the Wii firmware).

Once you boot up again, you'll need to use either a GameCube controller or the buttons on the Wii face (Power to move the cursor, Reset to select an option) on the bootscreen. You should back up your NAND memory (provided you've got 512MB free on the card; it's under the gears icon, then the icon with the arrow pointing from the chip to the SD card).

Next thing: install the Homebrew Browser.

Create an apps directory on the root of your SD card. Download the Homebrew Browser, extract it, and copy the homebrew_browser subdirectory to apps. Once it's on the SD card, you can load it from the Homebrew Channel; from there -- well, from there I got a stack dump and had to reload it. But I reloaded it, and from there you can download all sorts of useful apps -- including Savegame Extractor.

In fact, there are a few variations on it -- there's Savegame Manager, which combines Savegame Extractor with Savegame Installer, and which also just flat-out stack-dumped every time I tried to use it -- but there's a fork called SaveGame Manager EX, which works great, comes with a GUI that mimics the Wii's, and has a nice batch option to extract everything from the Wii at one go, eliminating all that tedious clicking on each individual file and then selecting Copy. (And, okay, also copying over some other shit that you don't really need to expend the space on backing up, like the Netflix Channel. But hey, still.)

Soapbox time: I'm not doing this to play pirated games. I'm not doing this to cheat at online games. (I'm not doing it to cheat at offline games, either, but if I were, that would be none of anybody's goddamn business but my own.)

I'm doing this to access my data, the games I bought and paid for (and, all right, one that Brent got me for my birthday), the saves I slogged through hours of stupid bullshit single-player Mario Kart to get.

And I shouldn't fucking have to install a bunch of hacks to do this.

I like my Wii. Rather a lot. I mean, Jesus Christ, look at how much effort I've gone to to keep all the stuff I've got on it, and that's before I've even started taking it apart.

But Nintendo is completely fucking ass-backwards in its approach to modern technology in general and network play in particular. Its "safeguards" are asinine and poorly-thought-out. They won't stop some guy with an Action Replay from unlocking all the karts on Mario Kart or all the fighters on Smash Bros and then going online (and hey, Nintendo? Maybe if you didn't make it impossible to unlock anything on multiplayer in Mario Kart, and a pain in the ass to unlock everything on multiplayer in Smash Bros, people wouldn't be tempted to cheat to do it?). They just put up barriers to prevent people with broken consoles from getting their data off. Which, again, includes games they paid for.

...and frankly they're not very good barriers. This was really a breeze. I'd like to thank the developers of all the various tools I've mentioned, and the writers of the walkthroughs on how to set them up. Because this was pretty damn painless, and to be frank I enjoyed doing it.

Tune in next time to see how I do at taking my Wii apart and seeing if I can fix it.

If I even get that far. I don't have a tri-wing screwdriver onhand, so I'm going to see if I can get the screws out with a small flathead. If not, well, tri-wings are like $5 on Amazon.

What "Hacker" Means to Me

Recently, I made some comments on the unfortunate change in popular usage of the word "hacker", from a positive term for a skilled programmer, to a negative term for a skilled programmer, to a negative term for someone who can figure out Sarah Palin's zip code.

I like to think of myself as a hacker in the original, positive sense, and I have a story about what that means.

Ten years ago, I upgraded my OS to Windows 98. Unfortunately, during the upgrade my hard drive, which had been compressed using DriveSpace, one of the worst pieces of software ever, was corrupted.

Now, I'll grant I'm a pack rat, but there wasn't much of sentimental value on there. There was, however, the most recent installment of KateStory, Book IX. It turned out Steve had a backup, but it was incomplete.

That gnawed at me for years. I kept the hard drive and never wiped it, and every now and again I'd hook it up and see if I could find a way to recover the data. I could never get it to mount. My instinct was that I shouldn't be working with the physical drive anyway, that I should copy the data from it to an image so I could make additional copies and freely mess with them without worrying about losing the original data. But none of the disk-imaging tools I could find would image a disk that wouldn't mount.

By the summer of 2004, I was familiar enough with Linux to know that dd was the tool I wanted, that it would make a bit-for-bit copy of the data on a device regardless of whether it could make any sense of it. I copied the drive to a file and went to take a look at what I could do with it.

File recovery software pulled up some images and some old E-Mails, but not the ones I wanted. In fact, searching the raw hex, I found the text "Subject: Re: KateStory IX: Third Anni" followed by gibberish; the data literally went from plain text to incomprehensible compressed bytes in the middle of the subject line I was looking for. I abandoned the project for a few months.

As the fall rolled around and the KateStory's tenth anniversary approached, I got to thinking about it again. I looked up information on how to recover DriveSpace volumes, and happened upon Dean Trower's DriveSpace 3 Disaster Recovery Kit. Since it required DriveSpace to run, and since DriveSpace won't run on modern versions of Windows, I set up VMWare on my computer and installed Windows 98 on it. My memory of what I tried then is fuzzy; I'm not sure what I did wrong but I still didn't recover the data.

It seems like I tried a couple more things over the years that followed. I think there was a period where I thought maybe the compression I couldn't get past wasn't DriveSpace's but Netscape's. (In retrospect, I believe Netscape Mail's "compress folders" option didn't actually compress text, it just deleted the text of E-Mails that had been deleted from the mailbox but not removed from the mail files.) I definitely remember at least one occasion where I dumped the entire 545MB hard drive image into a Thunderbird folder -- now, whether or not I qualify as a hacker, I think we can all agree that qualifies as a hack. When it didn't work under Thunderbird, I found old copies of Netscape 3 and 4 and tried it there; that didn't work either.

About a month ago, with KateStory XVII beginning, the anniversary approaching once more, and my going back through Books XIII-XVI to put them on this site, I got the urge to take another crack at IX. I did what I'd done before: set up VMWare, set up Windows 98, and got a copy of the Disaster Recovery Kit.

Without getting into too much detail, a DriveSpace "compressed drive" is actually a single file stored on a physical hard drive, then mounted as a virtual drive. As I said, I couldn't mount the drive. The docs for Trower's program mentioned creating an empty DriveSpace volume and looking at its file header; I got the idea from there to look at the header bytes on a fresh file and see where I could find them in my disk image. I found them -- the beginning of the compressed file -- and deleted everything prior to them on the image. (It bears noting that at this point I had numerous backups of the image and wasn't hacking up my only copy.)

Following the advice in Trower's Readme, I started with the simplest solution: copy the compressed file to a host drive and see if Windows mounts it. He cautioned that it might not work and Windows's attempt to "fix" the corrupted data could hose it; he was right. I was thrilled to see the filenames in the root directory show up, but I couldn't access the data in any of them.

On to step two: I tried using Trower's decmprss program. I tried it several times and discovered that it kept outputting empty files; they were the same size as my image but made up entirely of zeroes.

There was a line in the Readme: "DCMPRESS ought to work under Windows, but nevertheless I recommend running it in MS-DOS mode." All right. I did a Shut Down/Restart in MS-DOS Mode, but Windows 98 and VMWare weren't quite playing nice; any time I did that DOS would run for a minute or two and then freeze up and require a simulated hard reset.

So I went back to Windows, and checked to see why decmprss was outputting empty files. I started by trying it on a new compressed image that I knew didn't contain any corrupt data. I got the same result, proving that it wasn't just a problem reading my corrupt image.

Trower's toolkit included the source code, so I jumped into it to see if I could find out what was wrong. For the first time in years I found myself coding in Pascal -- coincidentally the same language Dr. Wily teaches at Prescott High School in KateStory IX. I didn't do anything particularly clever, just added some traces to see where the problem was occurring. I confirmed that the problem lay not in the Pascal portion of the code, but in the x86 assembler.

All right, I thought, my guess is that Windows 98 doesn't like the direct system calls that the assembler portion of the code is making. So that takes us back to trying to run it under DOS -- and if that doesn't work, the only thing left to try is to learn x86 assembler and pore through the DriveSpace API.

Booting to DOS from Win98 shutdown still didn't work, but it turned out that picking it from the boot menu worked just fine -- once I went into OSX's keyboard settings and disabled F8 for pulling up Spaces so I could use it in VMWare.

That worked, and generated a file that contained KateStory chapters that, I could confirm, were not in the copy I had.

That would be where the rest of Trower's toolkit came in -- reassembling files that had been partially compressed -- but I was confident that KateStory IX had been entirely compressed. So now it was time for my Thunderbird hack.

So I copied the entire, 1GB+ uncompressed image into Thunderbird's mail folders. Success -- Thunderbird correctly parsed out all the files that were E-Mails. I sorted them out, exported the ones that had "KateStory IX" in the sub line, and copied them out of the Win98 VM into my "real" system. From there I went through them all, cut out the stuff that was redundant or off-topic (which was most of it), and lo: today, this fourteenth anniversary of the original KateStory and eleventh anniversary of this installment, I have KateStory IX in its entirety.

So, back to my initial point: what does "hacker" mean to me? Well, eleven years ago my friends and I wrote a goofy story. Ten years ago, I lost it. And over the intervening years, I used my skill and my determination to get it back. (A friend once told me that when I want something I go after it like a pit bull, I don't let go. Comparisons to pit bulls may be the only thing Sarah Palin and I have in common.) I'm not some scary terrorist stealing your credit card or breaking into the Pentagon, I'm a guy who used his skill to recover a lost piece of his childhood.

Of course, I'm sure there are those who will say this doesn't make me a hacker. And maybe they're right. In the final analysis, all I did was use the dd command, set up a virtual machine, install Windows 98, do some very cursory hex editing, boot to DOS, use someone else's recovery tools, and copy a giant file into Thunderbird's mail folders. When all's said and done, I only wrote a few lines of code, and all they wound up doing was confirming what the Readme had already told me. So maybe that's not enough to qualify me as a hacker.

But you know what? If that's not enough to qualify as hacking, then plugging Sarah Palin's zip code into a password hint field sure as shit isn't.