resetBlackbox

Blackbox

Make the output "puzzlehunt2015"


You have used {{answerfield.length }}/45 Characters

Solution

author: Daniel Low

After playing with some random inputs, one will notice that certain characters will always append a new character while some others will modify the last character (and the last character only) by increasing or decreasing the value by 1.

For example, 9 will print c and 94 will print d and 944 will print e and so on.

It is then straight forward that the goal is to use fewer than 45 characters to produce the given output.

One way of solving this puzzle is to learn the pattern for each input character (hence the name blackbox), and construct the required value.

Another approach is to try and figure out what the javascript is doing and reverse engineer the algorithm.

Looking at the source code of the website and narrowing down to the core javascript file (blackbox/assets/main.min.js), we are faced with an obfuscated javascript file.

Searching for javascript deobfuscator and running the code through it, we get the following code:

var app = angular.module("blackbox", []);
app.controller("mainCtrl", ["$scope", function($scope) {
    (function() {
        function wow(doge) {
            var len = doge.length;
            var results = [];
            for (var i = 0; i < len; i++) {
                var c = doge.charCodeAt(i);
                if (c % 2 == 1) {
                    results[results.length] = c
                } else if (c % 8 == 0) {
                    results[results.length - 1] *= 2
                } else if (c % 4 == 0) {
                    results[results.length - 1]++
                } else {
                    results[results.length - 1]--
                }
                while (results[results.length - 1] < 0) {
                    results[results.length - 1] += 256
                }
                results[results.length - 1] = results[results.length - 1] % 256
            }
            return results
        }
        var functions = {
            a0: function(x, y) {
                return (x ^ y) % 256
            },
            a1: function(x, y) {
                return (x * y) % 256
            },
            a2: function(x, y) {
                return (x + y) % 256
            },
            a3: function(x, y) {
                y = y % 256;
                var result = (x - y) % 256;
                while (result < 0) {
                    result += 256
                }
                return result
            }
        };

        function much(doge) {
            if (doge) {
                var results = wow(doge);
                var lastval = 42;
                var value = "";
                for (var i = 0; i < results.length; i++) {
                    var f = "a" + (lastval % 4);
                    var newCode = functions[f](lastval, results[i]);
                    lastval = newCode;
                    value += String.fromCharCode(newCode)
                }
                return value
            }
        }
        $scope.processInput = function() {
            $scope.output = much($scope.answerfield)
        }
    })()
}])

With some javascript skills, one can reverse engineer the required solution.

As an example, ELAHLHNQL?LHHyHALHHLGLGHHL{HAHLH#H?HLHANHHLE is a valid solution although much more concise ones also exist, especially with creative use of unicode (as one team particularly impressed us with this puzzlehunt!).