Much Ado About Hacking
Writeup by hgarrereyn
- Reverse Engineering
- 165 points
- Description: In a lonely file, you find prose written in an interesting style. What is this Shakespearean play? What does it have to say? How could one get it to produce this ending?
From the title and the file extension
.spl, we are able to figure out that the provided file is written in the Shakespeare Programming Language, an esoteric programming language.
Our goal is to find the input to the program that outputs the provided ending:
tu1|\h+&g\[email protected]% :BH7M6m3g=
I had encountered this language before, but found it quite helpful to refer to the documentation while stepping though the program.
My plan to solve this challenge was twofold:
- compile an executable to test my inputs
- translate the program to python and reverse engineer it
Step 1 - compile the executable
spl2c to transpile the
spl file to a
c program. Then I used
gcc to compile.
It took quite a bit of fiddling to get the right compile flags and SPL version. I won't go into detail on exactly how I compiled because it's not exactly relevant to the problem.
However, this StackOverflow post was added as a hint and helped me figure out how to compile.
At this point I had a binary that could be run like so:
It seemed to wait for user input so I tried typing a few things in and hitting enter but the program didn't seem to print anything out.
Let's reverse engineer the code and see if we can figure out what's going on.
Step 2 - reverse engineering
To start, I simply went line by line and rewrote equivalent pseudo-python code. Note: The python code doesn't actually work, it just served as a useful reference
The first few lines simply define variables. For simplicity, we will use the same variable names in python:
Much Ado About Hacking. Benedick, a budding young hacker. Beatrice, a veteran exploiter. Don Pedro, a good friend of the others. Don John, he is just kinda there. Achilles, I thought he was from Greece. Cleopatra, now this is just getting ridiculous.
### Define variables (all integers): # benedick # beatrice # don_pedro # don_john # achilles # cleopatra
Now the program begins. In SPL, scenes act as subroutines that can be branched to.
Initially, no characters are on the stage. When a character enters the stage, deonoted by Enter and Exit, it can interact with other characters.
Commands are issued by writing the character's name followed by the command. The command is applied to the other character on the stage.
So for example, in the first few lines, we have:
Scene I: Benedick learns his place. [Enter Beatrice and Don John] Beatrice: You are nothing! [Exit Don John]
When Beatrice says "You are nothing!", it sets the value of Don John to zero (because he is the other actor on the stage). It is equivalent to:
don_john = 0
Then we see the same for Don Pedro:
[Enter Don Pedro] Beatrice: You are nothing! [Exit Don Pedro]
don_pedro = 0
Now in order to set a non-zero value, you have to use adjectives plus a noun. Each noun is either
-1 based on whether it is a positive or negative noun and each additional adjective multiplies that value by
In the lines:
[Enter Achilles] Beatrice: You are as proud as a bold brave gentle noble amazing hero. [Exit Achilles]
Achilles is set to the value of the string
bold brave gentle noble amazing hero.
Note: when a character says "You are as as ..." it means the same as "You are equal to ..."*
Hero is a positive noun, so it has the value
1. Then there are 5 adjectives that each multiply by
2. So our final value is:
2^5 = 32.
Therefore, equivalent python is:
achilles = 32
In the next lines, Cleopatra is set:
[Enter Cleopatra] Beatrice: You are as vile as the difference between a beautiful gentle bold fair peaceful sunny lovely flower and Achilles. [Exit Cleopatra]
In this case, a subtraction operation is performed. Cleopatra is set to the value of
beautiful gentle bold fair peaceful sunny lovely flower minus the value of
Achilles. In this case, Achilles corresponds to a variable and not the value
beautiful gentle bold fair peaceful sunny lovely flower =
So Cleopatra is set to
128 - 32 =
cleopatra = 96
Our first scene looks like this:
# Scene I: don_john = 0 don_pedro = 0 achilles = 32 cleopatra = 96 benedick = 0
Benedick and Beatrice are still on stage from the last scene. Let's examine the first command:
Beatrice: Open your mind! Remember yourself.
Here she issues two commands:
Open your mind!- reads a character from input and sets Benedick to it's integer value
Remember yourself.- pushes the value of Benedick onto his stack
Now, I omitted this before, but all characters have an integer stack that they can push and pop values from. So, in our initialization, let's create an array that represents Benedick's stack:
benedick_stack = 
So these lines translate to:
benedick = readchar() benedick_stack.append(benedick)
The next lines are:
Benedick: You are as red as the sum of yourself and a tree. Am I as lovely as a cunning charming honest peaceful bold pony? Beatrice: If not, let us return to scene II.
Which translate to:
beatrice += 1 if not benedick == 32: goto: S2
So these lines effectively cause the input to loop reading characters until a character is read with the value
32. That is the space character in ascii:
' ' == 32.
If we don't jump back, we see:
Benedick: You are as worried as the sum of yourself and a Microsoft. Beatrice: Recall your father's disappointment!
Note: Microsoft is a negative noun ;p
beatrice -= 1 benedick = benedick_stack.pop()
Beatrice: Recall your latest blunder! You are as smelly as the difference between yourself and Achilles. Benedick: You are as disgusting as the sum of yourself and a flirt-gill! [Exit Beatrice] [Enter Don John] Benedick: Thou art as damned as I. [Exit Don John] [Enter Don Pedro] Don Pedro: You are as rotten as the sum of yourself and myself. Thou art as normal as the remainder of the quotient between thyself and Cleopatra. [Exit Benedick] [Enter Don John] Don John: You are as good as I. [Exeunt] [Enter Beatrice and Benedick] Beatrice: You are as foul as the sum of yourself and Achilles. Speak your mind! Benedick: Are you better than nothing? Beatrice: If so, let us return to scene III. [Exeunt]
Here we see that
beatrice is used as a counter to pop values off of the
benedick. Then a few operations are done on the character before it is printed. The following python lines are equivelant:
# S3: benedick = benedick_stack.pop() benedick = benedick - achilles beatrice = beatrice - 1 don_john = benedrick benedrick = benedrick + don_pedro benedrick = benedrick % cleopatra don_pedro = don_john benedrick = benedrick + achilles print(benedrick) if beatrice > 0: # GOTO: S3
This python code is still pretty cryptic. It might make more sense if we display this as a flow-graph. We see that the input is processed in reverse and we have a memory cell (
don_pedro) that is applied to the next character each time.
Now that we understand how the forward pass works, let's see if we can write a program that takes the output and reconstructs the input.
Note: this is slightly more difficult due to the mod operator not being bidirectional. We can get around that by assuming that the input to the mod operator would be in the range
[0,191). Therefore, if we have a positive number at that position, do nothing, and if we have a negative number, add
# By Harrison Green <hgarrereyn> # The output we want to recreate out = raw_input() # Get the output values outVal = [ord(x) for x in out] # The memory operator (initially zero) b = 0 # Initialize a new array the size of the output string c =  * len(out) for i in range(0, len(out)): # First pass and disassociate from memory operator c[i] = outVal[i] - 32 - b # Reverse the mod function by assuming that inputs were in # range [0,192) if c[i] < 0: c[i] = c[i] + 96 # Memory storage b = c[i] # Final pass c[i] = c[i] + 32 # Reverse array and convert back to string in_str = "".join([chr(x) for x in c][::-1]) # Print print(in_str)
Note: it does not output the trailing space as that was used only to signify the end of the input string
We can test if the script works by encoding and decoding some test strings:
$ echo "some_random_string " | ./muchado | python reconstruct.py some_random_string
Cool, it works!
Now let's run it on the ending we were provided:
$ cat ending.txt | python reconstruct.py [email protected]!t
We submit that as our flag.