Steins;Gate's mysterious Email ARG

I've been enjoying the Steins;Gate visual novel recently. At the start of the game, if you check your in-game phone's inbox, you'll see an email. A special, weird, encoded email that reeks of secrets. It's been bothering me all throughout my first playthrough. So, I dug in a bit to see if I could figure anything out. 5 hours later I was still quite confused but at least I knew more about character encodings than when I started.

The nerd snipe

If you've never heard the phrase "nerdsniped", then take 2 seconds to update your vocabulary by reading the following XKCD comic:

XKCD comic 356 the nerdsnipe

When I first started up the Steins;Gate game and the tutorial told me to open my phone and check my email, the first thing that stood out to me was that I already had an email. And... it looked kind of funny:

While you can't see the bottom of that email in one window, if you scroll the contents of it are the following:

///////////////////////
///////////////////////
////////////7MU2Et9HuKf
Pz4ifIT3CBSkeqxLsEnFVVj
26u7NieXQOcBzOxXl3bMbHH
TzDSKhRtyJM8CXAqqwYJpsC
AOBiWdcxZfQ9HKmHfREsSip
KPwSdmUNHmtKGcFiudktOiO
Y5KCFn9CORVY8sFcMF3RISl
hoNvfrmjVgH41WhJDVOWuzv
VdwlzOJjvLUI9vOqTzZIyd9
b1iDi3niJHDMnxMxLeLoW5F
aWPgXjl2MS1vvoRpC54NE4F
r7tmsgcZdAIq3KOXJsACNXn
rUVGTWxfQZL3fp9QZm3oKyi
PNN5TeRRzoJ9DBy2Hx3vRb4
Bxi4KAoi4Qu528ySaJrEppq
8n9ITveVE7gdQkgQuyPJDId
4o7b3SaAIfpFP2PNYFGPxrQ
oULgxas7t7NKGEREXkMSCOn
cf6QXaZc6mcw4E51PaJn47P
h62pAnqB55sSXMiDXmQc4D2
BBUtJuS3h9iQbkLctfaAgh0
Yo7vSeFp3wEXA8EvDmI3tcd
psHxA4yZSzM3CCScPLwhu3d
OgHVLApyb9MIXaiI8FRwHxF
ETLTUByT9MAvtNsygVDrLbp
g2i1feALSCwK4teLw2CTBRf
X5lwTHKzEhRtyB3xBWEAVrp
4BnEK6LmHuCkwV4KqyaG1hV
dn5SRVkR53nUj61iLhFREcj
aXTElE2WO5SdGGkaVlZMVzZ
gu0yQVWisHvSvfsmegK8TYu
4O9XOzHSYdsQnhW6OQKcnZp
uBuNrb4zx8IZUn8OUwWk0YI
jNnucs1CJ04u4a9YdcCFKPO
C3do5Ri9SHVzcYOUI00mmg9
GimMTxnUn4Lz3LU4Wzei0//
///////////////////////
///////////////////////
///////
=?ISO-2022-JP?B?GyRCJDMkbCRDJEZDLyQrMnJGSSRHJC0kRiQ/JGokNyReJDkhKRsoQg==?=

Mysterious. Oddly enough, the game doesn't mention it at all, makes no note about it, no idle musing from the main character Okabe Rintaro, and no one ever brings it up either. He never seems to notice that it wasn't in our phone when we started the game, and then, when we shifted world lines after the initial setup for the plot, it suddenly appeared.

But. I was very curious, and today, after collecting 3 endings of the game so far, I decided to sit down and try to understand it. Here's my notes:

Nerd successfully sniped (me)

Now, I've done plenty of web work in my career, and so seeing a few trailing == at the end of some "mysterious" text immediately throw up my "Oh hey look, it's base64encoded" senses. If I showed anyone this:

GyRCJDMkbCRDJEZDLyQrMnJGSSRHJC0kRiQ/JGokNyReJDkhKRsoQg==

They'd immediately toss it into a decoder. The problem of course, is that if you just blindly do this on something like base64decode.org then you'll get this trash:

$B$3$l$C$FC/$+2rFI$G$-$F$?$j$7$^$9!)(B

Because the defaults for the website are for utf-8. That said, if we look at the rest of the message we can see that the "tags" =? ?= were wrapping more than just that. In fact, there's pretty obvious ASCII text right there saying

ISO-2022-JP?B?

If we assume that the B stands for Base 64, then it stands to reason that the other encoding, ISO-2022-JP, is what this mysterious sequence of bytes is. And, if you were to swap the website's decode mode, you'll see the following text:

これって誰か解読できてたりします?
Kore tte dare ka kaidoku dekite tari shimasu?
Can anyone decipher this?

The first line is the direct decode, while the next two are just a quick romanji and English translation.

That said. When I first was looking at this, I didn't actually notice the dropdown for character set on the website and thought I was out of luck and I'd have to figure out how to do the decoding myself. So, the majority of these last 5 hours was spent in Java world writing code like this:

final String hint = "GyRCJDMkbCRDJEZDLyQrMnJGSSRHJC0kRiQ/JGokNyReJDkhKRsoQg==";
System.out.println("HINT");
byte[] decoded = Base64.getDecoder().decode(hint);
for (byte rawByte : decoded) {
    System.out.print(" ");
    System.out.print(rawByte);
    System.out.print(" ");
}
System.out.println(new String(decoded, Charset.forName("ISO-2022-JP")));

The last line, you'd expect to print out the Japanese characters, and yet, it doesn't. It prints out ????????????????? and no amount of fiddling with my terminal settings, changing fonts, or anything else seemed to change that. So I was left looking at the raw bytes instead.

27  36  66
36  51  36  108  36  67  36  70  67  47  36  43  50  114  70  73  36  71  36  45  36  70  36  63  36  106  36  55  36  94  36  57  33  41
27  40  66

Now, given that I knew it was supposed to be ISO-2022-JP, I looked it up! RFC1468 defines the ISO-2022-JP standard. Which is pretty interesting. I haven't really gone deep into the world of character encodings before, but while I was reading this a few things immediately stood out that I'll highlight.

In the Steins;Gate game, they talk about the structure of the D-MAIL that's sent and the limitations thereof. They're only able to send 32 bytes of information to the past via D-MAIL, and it's delivered as 12 byte blocks. Or, as 3 emails with 12 bytes in it. They discuss how many words they can send and note that English text using ASCII can be sent a bit easier than Japanese because Japanese uses two bytes to encode while english only uses 1. So I was looking at things two bytes at a time.

27 36 if we look them up in the ASCII table are the ESC control code and a $. Now check out this note from the RFC:

The text starts in ASCII, and switches to Japanese characters through an escape sequence. For example, the escape sequence ESC $ B (three bytes, hexadecimal values: 1B 24 42) indicates that the bytes following this escape sequence are Japanese characters, which are encoded in two bytes each. To switch back to ASCII, the escape sequence ESC ( B is used.

Emphasis mine. So, I've got the start of an escape sequence, but what's the third byte? 66 And in ASCII? 66 is a B. Perfect. So I know I've got an escape sequence. What kind? Looking at the RFC again, the format being described actually allows more than one character set inside of it, determined by escape sequences:

Esc Seq    Character Set                  ISOREG

ESC ( B    ASCII                             6
ESC ( J    JIS X 0201-1976 ("Roman" set)    14
ESC $ @    JIS X 0208-1978                  42
ESC $ B    JIS X 0208-1983                  87

A form of JIS (Japanese Industrial Standards) encoding it looks like! Further down in the RFC there's an important note for when you're reading in the different modes:

If there are JIS X 0208 characters on a line, there must be a switch to ASCII or to the "Roman" set of JIS X 0201 before the end of the line (i.e., before the CRLF). This means that the next line starts in the character set that was switched to before the end of the previous line.

So we'll always have a mix of character sets, and we should expect that the ending of a line should finish with the escape sequence to go to ASCII. Does our data follow that? The last three bytes of this single line message are:

27  40  66

Or in character form ESC ( B, which is the correct sequence! Great, so that's a pretty strong reassurance that, yes, we do in fact have ISO-2022-JP data in our hands. So each pair of bytes inbetween these escape sequences should be the encoding of a Japanese Character!

So I focused my efforts onto the first pair: 36, 51 Unfortunately for me, I wasn't really sure what to do with them or how to combine them right away. So, I looked it up on Wikipedia

In order to represent code points, column/line numbers are used for one-byte codes and kuten numbers are used for two-byte codes. For a way to identify a character without depending on a code, character names are used.

Ok...

The double-byte codes are laid out in 94 numbered groups, each called a row (区, ku, lit. "section"). Every row contains 94 numbered codes, each called a cell (点, ten, lit. "point"). This makes a total of 8836 (94 x 94) possible code points (although not all are assigned, see below); these are laid out in the standard in a 94-line, 94-column code table

Well, it sounds like I probably want to find this table! If I just need to treat each byte as a pair for a row and column, then if I can figure that out we'll be great. After 30 minutes of googling for the table, repeatedly being taken to wikipedia pages with no actual links to the table itself and just descriptions and examples of each, I finally found myself on this page on unicode.org

And wouldn't you know it, I got pretty lucky because reading the comment section at the top supplied me with this information:

#	Format:  Four tab-separated columns
#		 Column #1 is the shift-JIS code (in hex)
#		 Column #2 is the JIS X 0208 code (in hex as 0xXXXX)
#		 Column #3 is the Unicode (in hex as 0xXXXX)
#		 Column #4 the Unicode name (follows a comment sign, '#')
#			The official names for Unicode characters U+4E00
#			to U+9FA5, inclusive, is "CJK UNIFIED IDEOGRAPH-XXXX",
#			where XXXX is the code point.  Including all these
#			names in this file increases its size substantially
#			and needlessly.  The token "<CJK>" is used for the
#			name of these characters.  If necessary, it can be
#			expanded algorithmically by a parser or editor.
#
#	The entries are in JIS X 0208 order
#
#	The following algorithms can be used to change the hex form
#		of JIS 0208 to other standard forms:
#
#		To change hex to EUC form, add 0x8080
#		To change hex to kuten form, first subtract 0x2020.  Then
#			the high and low bytes correspond to the ku and ten of
#			the kuten form.  For example, 0x2121 -> 0x0101 -> 0101;
#			0x7426 -> 0x5406 -> 8406

I started off on the wrong foot here, getting confused about encoding or decoding. I was thinking to myself, that I had a row and line potentially, so 36 and 51 needed to have their higher and lower bits combined. 0x24 0x33 squished together into 0x2433 and then have x2020 bitwise added to get 0x4453 but... after beating my head against things for a while, I realized that the data is already encoded isn't it?

Which means that the two bytes I have should already be in the order of the table. Which means that the first byte 0x24 is pointing to row 4 (0x24 - 0x20), and second byte 0x33 to columm 20 (0x33 - 0x20). The character こ. So all I truly have to do is the lookup in the unicode table site, no math required! To make my life easier, I cut the unicode file down a bit into just the information I cared about:

cut -d "      " -f2,4- JIS0208.TXT

Then saved it to mappings.txt before running the following Java code:

final String hint = "GyRCJDMkbCRDJEZDLyQrMnJGSSRHJC0kRiQ/JGokNyReJDkhKRsoQg==";
byte[] decoded = Base64.getDecoder().decode(hint);
List<String> mappings = Files.readAllLines(Paths.get("mappings.txt"));
for (int i = 3; i < decoded.length - 4; i += 2) {
    int row = i;
    int col = i + 1;
    for (String mapping : mappings) {
        if (mapping.contains(String.format("0x%X%X", decoded[row], decoded[col]))) {
            System.out.println(mapping);
        }
    }
}
                

And what did I get?

0x2433  # HIRAGANA LETTER KO
0x246C  # HIRAGANA LETTER RE
0x2443  # HIRAGANA LETTER SMALL TU
0x2446  # HIRAGANA LETTER TE
0x432F  # <CJK>
0x242B  # HIRAGANA LETTER KA
0x3272  # <CJK>
0x4649  # <CJK>
0x2447  # HIRAGANA LETTER DE
0x242D  # HIRAGANA LETTER KI
0x2446  # HIRAGANA LETTER TE
0x243F  # HIRAGANA LETTER TA
0x246A  # HIRAGANA LETTER RI
0x2437  # HIRAGANA LETTER SI
0x245E  # HIRAGANA LETTER MA
0x2439  # HIRAGANA LETTER SU
0x2129  # FULLWIDTH QUESTION MARK

This is looking promising, but, what's that CJK thing?

Including all these names in this file increases its size substantially and needlessly. The token "<CJK>" is used for the name of these characters

Oh, I suppose that's... weird. I was hoping that unicode table would have everything in it without an extra hop, but it does look like we've only got 3, and we can look them up on wikipedia here. Except for the fact that these are in kuten form, so we need to convert the data:

0x432F -> 0x230F -> 0x23 0x0F -> row 35, col 15 -> 誰
0x3272 -> 0x1252 -> 0x12 0x52 -> row 18, col 82 -> 解
0x4649 -> 0x2629 -> 0x26 0x29 -> row 38, col 41 -> 読

And with that, we can take the list of mappings, the kanji, and write out the real sentence again in Japanese:

これって誰か解読できてたりします?

Which is just Rintaro asking us for help in decoding the other payload in the email.

The payload

So, we've got a way to read the bytes out, and we did it based on the "tags" of =? ?= around the text. Looking at the rest of the email I can see we've got:

///////////////////////
///////////////////////
////////////7MU2Et9HuKf
Pz4ifIT3CBSkeqxLsEnFVVj
...
C3do5Ri9SHVzcYOUI00mmg9
GimMTxnUn4Lz3LU4Wzei0//
///////////////////////
///////////////////////
///////

Which feels like perhaps the long string of //// could also be some form of padding. But... If we count how many / on the front exist, versus how many are open on the back, we get 58 and 55. Which doesn't really match up.

That said, this is an email sent to us from Rintaro, and he was paranoid enough to base64 encode the data once before it had to be converted to text for us to read via a character encoding lookup. So maybe it's also Base 64 encoded and then full of something else? So I gave it a try, assuming that the full message was one big string, and that there were no newlines:

decoded = Base64.getDecoder().decode(msg);
int i = 0;
for (byte rawByte : decoded) {
    System.out.printf(" 0x%02X ", rawByte);
    i++;
    if (i > 23) {
        System.out.println();
        i = 0;
    }
}

And surprisingly enough, it did actually decode! Printing this off with 23 characters per line gets us quite the... thing to look at. I mean, there's 927 characters here, an odd number.

0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF
0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFE  0xCC  0x53  0x61  0x2D
0xF4  0x7B  0x8A  0x7C  0xFC  0xF8  0x89  0xF2  0x13  0xDC  0x20  0x52  0x91  0xEA  0xB1  0x2E  0xC1  0x27  0x15  0x55  0x63  0xDB  0xAB  0xBB
0x36  0x27  0x97  0x40  0xE7  0x01  0xCC  0xEC  0x57  0x97  0x76  0xCC  0x6C  0x71  0xD3  0xCC  0x34  0x8A  0x85  0x1B  0x72  0x24  0xCF  0x02
0x5C  0x0A  0xAA  0xC1  0x82  0x69  0xB0  0x20  0x0E  0x06  0x25  0x9D  0x73  0x16  0x5F  0x43  0xD1  0xCA  0x98  0x77  0xD1  0x12  0xC4  0xA2
0xA4  0xA3  0xF0  0x49  0xD9  0x94  0x34  0x79  0xAD  0x28  0x67  0x05  0x8A  0xE7  0x64  0xB4  0xE8  0x8E  0x63  0x92  0x82  0x16  0x7F  0x42
0x39  0x15  0x58  0xF2  0xC1  0x5C  0x30  0x5D  0xD1  0x21  0x29  0x61  0xA0  0xDB  0xDF  0xAE  0x68  0xD5  0x80  0x7E  0x35  0x5A  0x12  0x43
0x54  0xE5  0xAE  0xCE  0xF5  0x5D  0xC2  0x5C  0xCE  0x26  0x3B  0xCB  0x50  0x8F  0x6F  0x3A  0xA4  0xF3  0x64  0x8C  0x9D  0xF5  0xBD  0x62
0x0E  0x2D  0xE7  0x88  0x91  0xC3  0x32  0x7C  0x4C  0xC4  0xB7  0x8B  0xA1  0x6E  0x45  0x69  0x63  0xE0  0x5E  0x39  0x76  0x31  0x2D  0x6F
0xBE  0x84  0x69  0x0B  0x9E  0x0D  0x13  0x81  0x6B  0xEE  0xD9  0xAC  0x81  0xC6  0x5D  0x00  0x8A  0xB7  0x28  0xE5  0xC9  0xB0  0x00  0x8D
0x5E  0x7A  0xD4  0x54  0x64  0xD6  0xC5  0xF4  0x19  0x2F  0x77  0xE9  0xF5  0x06  0x66  0xDE  0x82  0xB2  0x88  0xF3  0x4D  0xE5  0x37  0x91
0x47  0x3A  0x09  0xF4  0x30  0x72  0xD8  0x7C  0x77  0xBD  0x16  0xF8  0x07  0x18  0xB8  0x28  0x0A  0x22  0xE1  0x0B  0xB9  0xDB  0xCC  0x92
0x68  0x9A  0xC4  0xA6  0x9A  0xBC  0x9F  0xD2  0x13  0xBD  0xE5  0x44  0xEE  0x07  0x50  0x92  0x04  0x2E  0xC8  0xF2  0x43  0x21  0xDE  0x28
0xED  0xBD  0xD2  0x68  0x02  0x1F  0xA4  0x53  0xF6  0x3C  0xD6  0x05  0x18  0xFC  0x6B  0x42  0x85  0x0B  0x83  0x16  0xAC  0xEE  0xDE  0xCD
0x28  0x61  0x11  0x11  0x79  0x0C  0x48  0x23  0xA7  0x71  0xFE  0x90  0x5D  0xA6  0x5C  0xEA  0x67  0x30  0xE0  0x4E  0x75  0x3D  0xA2  0x67
0xE3  0xB3  0xE1  0xEB  0x6A  0x40  0x9E  0xA0  0x79  0xE6  0xC4  0x97  0x32  0x20  0xD7  0x99  0x07  0x38  0x0F  0x60  0x41  0x52  0xD2  0x6E
0x4B  0x78  0x7D  0x89  0x06  0xE4  0x2D  0xCB  0x5F  0x68  0x08  0x21  0xD1  0x8A  0x3B  0xBD  0x27  0x85  0xA7  0x7C  0x04  0x5C  0x0F  0x04
0xBC  0x39  0x88  0xDE  0xD7  0x1D  0xA6  0xC1  0xF1  0x03  0x8C  0x99  0x4B  0x33  0x37  0x08  0x24  0x9C  0x3C  0xBC  0x21  0xBB  0x77  0x4E
0x80  0x75  0x4B  0x02  0x9C  0x9B  0xF4  0xC2  0x17  0x6A  0x22  0x3C  0x15  0x1C  0x07  0xC4  0x51  0x13  0x2D  0x35  0x01  0xC9  0x3F  0x4C
0x02  0xFB  0x4D  0xB3  0x28  0x15  0x0E  0xB2  0xDB  0xA6  0x0D  0xA2  0xD5  0xF7  0x80  0x2D  0x20  0xB0  0x2B  0x8B  0x5E  0x2F  0x0D  0x82
0x4C  0x14  0x5F  0x5F  0x99  0x70  0x4C  0x72  0xB3  0x12  0x14  0x6D  0xC8  0x1D  0xF1  0x05  0x61  0x00  0x56  0xBA  0x78  0x06  0x71  0x0A
0xE8  0xB9  0x87  0xB8  0x29  0x30  0x57  0x82  0xAA  0xC9  0xA1  0xB5  0x85  0x57  0x67  0xE5  0x24  0x55  0x91  0x1E  0x77  0x9D  0x48  0xFA
0xD6  0x22  0xE1  0x15  0x11  0x1C  0x8D  0xA5  0xD3  0x12  0x51  0x36  0x58  0xEE  0x52  0x74  0x61  0xA4  0x69  0x59  0x59  0x31  0x5C  0xD9
0x82  0xED  0x32  0x41  0x55  0xA2  0xB0  0x7B  0xD2  0xBD  0xFB  0x26  0x7A  0x02  0xBC  0x4D  0x8B  0xB8  0x3B  0xD5  0xCE  0xCC  0x74  0x98
0x76  0xC4  0x27  0x85  0x6E  0x8E  0x40  0xA7  0x27  0x66  0x9B  0x81  0xB8  0xDA  0xDB  0xE3  0x3C  0x7C  0x21  0x95  0x27  0xF0  0xE5  0x30
0x5A  0x4D  0x18  0x22  0x33  0x67  0xB9  0xCB  0x35  0x08  0x9D  0x38  0xBB  0x86  0xBD  0x61  0xD7  0x02  0x14  0xA3  0xCE  0x0B  0x77  0x68
0xE5  0x18  0xBD  0x48  0x75  0x73  0x71  0x83  0x94  0x23  0x4D  0x26  0x9A  0x0F  0x46  0x8A  0x63  0x13  0xC6  0x75  0x27  0xE0  0xBC  0xF7
0x2D  0x4E  0x16  0xCD  0xE8  0xB4  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF
0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0xFF

Which feels a bit odd since everything so far was even. Even though control codes resulted in an odd number of bytes, their symmetry made it add up to even again afterwards. Out of curiousity, and returning to my previous hunch about the / being padding, I removed it and checked to see if the data would decode:

0xEC  0xC5  0x36  0x12  0xDF  0x47  0xB8  0xA7  0xCF  0xCF  0x88  0x9F  0x21  0x3D  0xC2  0x05  0x29  0x1E  0xAB  0x12  0xEC  0x12  0x71  0x55
0x56  0x3D  0xBA  0xBB  0xB3  0x62  0x79  0x74  0x0E  0x70  0x1C  0xCE  0xC5  0x79  0x77  0x6C  0xC6  0xC7  0x1D  0x3C  0xC3  0x48  0xA8  0x51
0xB7  0x22  0x4C  0xF0  0x25  0xC0  0xAA  0xAC  0x18  0x26  0x9B  0x02  0x00  0xE0  0x62  0x59  0xD7  0x31  0x65  0xF4  0x3D  0x1C  0xA9  0x87
0x7D  0x11  0x2C  0x4A  0x2A  0x4A  0x3F  0x04  0x9D  0x99  0x43  0x47  0x9A  0xD2  0x86  0x70  0x58  0xAE  0x76  0x4B  0x4E  0x88  0xE6  0x39
0x28  0x21  0x67  0xF4  0x23  0x91  0x55  0x8F  0x2C  0x15  0xC3  0x05  0xDD  0x12  0x12  0x96  0x1A  0x0D  0xBD  0xFA  0xE6  0x8D  0x58  0x07
0xE3  0x55  0xA1  0x24  0x35  0x4E  0x5A  0xEC  0xEF  0x55  0xDC  0x25  0xCC  0xE2  0x63  0xBC  0xB5  0x08  0xF6  0xF3  0xAA  0x4F  0x36  0x48
0xC9  0xDF  0x5B  0xD6  0x20  0xE2  0xDE  0x78  0x89  0x1C  0x33  0x27  0xC4  0xCC  0x4B  0x78  0xBA  0x16  0xE4  0x56  0x96  0x3E  0x05  0xE3
0x97  0x63  0x12  0xD6  0xFB  0xE8  0x46  0x90  0xB9  0xE0  0xD1  0x38  0x16  0xBE  0xED  0x9A  0xC8  0x1C  0x65  0xD0  0x08  0xAB  0x72  0x8E
0x5C  0x9B  0x00  0x08  0xD5  0xE7  0xAD  0x45  0x46  0x4D  0x6C  0x5F  0x41  0x92  0xF7  0x7E  0x9F  0x50  0x66  0x6D  0xE8  0x2B  0x28  0x8F
0x34  0xDE  0x53  0x79  0x14  0x73  0xA0  0x9F  0x43  0x07  0x2D  0x87  0xC7  0x7B  0xD1  0x6F  0x80  0x71  0x8B  0x82  0x80  0xA2  0x2E  0x10
0xBB  0x9D  0xBC  0xC9  0x26  0x89  0xAC  0x4A  0x69  0xAB  0xC9  0xFD  0x21  0x3B  0xDE  0x54  0x4E  0xE0  0x75  0x09  0x20  0x42  0xEC  0x8F
0x24  0x32  0x1D  0xE2  0x8E  0xDB  0xDD  0x26  0x80  0x21  0xFA  0x45  0x3F  0x63  0xCD  0x60  0x51  0x8F  0xC6  0xB4  0x28  0x50  0xB8  0x31
0x6A  0xCE  0xED  0xEC  0xD2  0x86  0x11  0x11  0x17  0x90  0xC4  0x82  0x3A  0x77  0x1F  0xE9  0x05  0xDA  0x65  0xCE  0xA6  0x73  0x0E  0x04
0xE7  0x53  0xDA  0x26  0x7E  0x3B  0x3E  0x1E  0xB6  0xA4  0x09  0xEA  0x07  0x9E  0x6C  0x49  0x73  0x22  0x0D  0x79  0x90  0x73  0x80  0xF6
0x04  0x15  0x2D  0x26  0xE4  0xB7  0x87  0xD8  0x90  0x6E  0x42  0xDC  0xB5  0xF6  0x80  0x82  0x1D  0x18  0xA3  0xBB  0xD2  0x78  0x5A  0x77
0xC0  0x45  0xC0  0xF0  0x4B  0xC3  0x98  0x8D  0xED  0x71  0xDA  0x6C  0x1F  0x10  0x38  0xC9  0x94  0xB3  0x33  0x70  0x82  0x49  0xC3  0xCB
0xC2  0x1B  0xB7  0x74  0xE8  0x07  0x54  0xB0  0x29  0xC9  0xBF  0x4C  0x21  0x76  0xA2  0x23  0xC1  0x51  0xC0  0x7C  0x45  0x11  0x32  0xD3
0x50  0x1C  0x93  0xF4  0xC0  0x2F  0xB4  0xDB  0x32  0x81  0x50  0xEB  0x2D  0xBA  0x60  0xDA  0x2D  0x5F  0x78  0x02  0xD2  0x0B  0x02  0xB8
0xB5  0xE2  0xF0  0xD8  0x24  0xC1  0x45  0xF5  0xF9  0x97  0x04  0xC7  0x2B  0x31  0x21  0x46  0xDC  0x81  0xDF  0x10  0x56  0x10  0x05  0x6B
0xA7  0x80  0x67  0x10  0xAE  0x8B  0x98  0x7B  0x82  0x93  0x05  0x78  0x2A  0xAC  0x9A  0x1B  0x58  0x55  0x76  0x7E  0x52  0x45  0x59  0x11
0xE7  0x79  0xD4  0x8F  0xAD  0x62  0x2E  0x11  0x51  0x11  0xC8  0xDA  0x5D  0x31  0x25  0x13  0x65  0x8E  0xE5  0x27  0x46  0x1A  0x46  0x95
0x95  0x93  0x15  0xCD  0x98  0x2E  0xD3  0x24  0x15  0x5A  0x2B  0x07  0xBD  0x2B  0xDF  0xB2  0x67  0xA0  0x2B  0xC4  0xD8  0xBB  0x83  0xBD
0x5C  0xEC  0xC7  0x49  0x87  0x6C  0x42  0x78  0x56  0xE8  0xE4  0x0A  0x72  0x76  0x69  0xB8  0x1B  0x8D  0xAD  0xBE  0x33  0xC7  0xC2  0x19
0x52  0x7F  0x0E  0x53  0x05  0xA4  0xD1  0x82  0x23  0x36  0x7B  0x9C  0xB3  0x50  0x89  0xD3  0x8B  0xB8  0x6B  0xD6  0x1D  0x70  0x21  0x4A
0x3C  0xE0  0xB7  0x76  0x8E  0x51  0x8B  0xD4  0x87  0x57  0x37  0x18  0x39  0x42  0x34  0xD2  0x69  0xA0  0xF4  0x68  0xA6  0x31  0x3C  0x67
0x52  0x7E  0x0B  0xCF  0x72  0xD4  0xE1  0x6C  0xDE  0x8B                   

To my surprise, it did. And more importantly, we had an even number of pairs again. That said, trying to put this data through a direct conversion tool to ISO-2022-JP returned some glitchy nonsense.

ナ6゚Gクァママ!=ツ)ォqUV=コサウbytpホナywlニヌシテネィムキ「フ・タェャヲ�bルラアeスゥ}ャハェハソテヌメpリョvヒホケィ。g」ユャテン
スリユ。、オホレユワ・フcシオェマカネノ゚ロヨ ゙xウァトフヒxコヨセcヨニケムクセネeミォrワ�ユュナニヘl゚チ~ミfmォィエ゙モysテュヌ{ムoq「ョサシノヲャハiォノ。ザヤホu	 ツ、イロンヲ。ナソcヘ`ムニエィミクアjホメトコwレeホヲsモレヲ~サセカ、	lノs「
ysュヲキリnツワオ」サメxレwタナタヒテqレlクノウウpノテヒツキtヤーゥノソフ。v「」チムタ|ナイモミタッエロイミュコ`レュ゚xメクオリ、チナヌォア。ニワ゚ヨkァgョ{xェャリユv~メナルyヤュbョムネレンア・eァニニヘョモ、レォスォ゚イgォトリサスワヌノlツxヨ
rviクュセウヌツメモ、ム」カ{ウミモクkヨp。ハシキvムヤラキケツエメihヲアシgメ~マrヤl゙

Sure, there was a smattering of japanese characters mixed in, but nothing that looked like real Japanese at all. Out of curiousity, I dumped it out not as hex, but as decimal to see if I could observe any patterns in it:

No padding! Payload string decode as Base64, 12 per line
-20     -59      54      18     -33      71     -72     -89     -49     -49    -120     -97      33
 61     -62       5      41      30     -85      18     -20      18     113      85      86      61
-70     -69     -77      98     121     116      14     112      28     -50     -59     121     119
108     -58     -57      29      60     -61      72     -88      81     -73      34      76     -16
 37     -64     -86     -84      24      38    -101       2       0     -32      98      89     -41
 49     101     -12      61      28     -87    -121     125      17      44      74      42      74
 63       4     -99    -103      67      71    -102     -46    -122     112      88     -82     118
 75      78    -120     -26      57      40      33     103     -12      35    -111      85    -113
 44      21     -61       5     -35      18      18    -106      26      13     -67      -6     -26
-115      88       7     -29      85     -95      36      53      78      90     -20     -17      85
-36      37     -52     -30      99     -68     -75       8     -10     -13     -86      79      54
 72     -55     -33      91     -42      32     -30     -34     120    -119      28      51      39
-60     -52      75     120     -70      22     -28      86    -106      62       5     -29    -105
 99      18     -42      -5     -24      70    -112     -71     -32     -47      56      22     -66
-19    -102     -56      28     101     -48       8     -85     114    -114      92    -101       0
  8     -43     -25     -83      69      70      77     108      95      65    -110      -9     126
-97      80     102     109     -24      43      40    -113      52     -34      83     121      20
115     -96     -97      67       7      45    -121     -57     123     -47     111    -128     113
-117    -126    -128     -94      46      16     -69     -99     -68     -55      38    -119     -84
 74     105     -85     -55      -3      33      59     -34      84      78     -32     117       9
 32      66     -20    -113      36      50      29     -30    -114     -37     -35      38    -128
 33      -6      69      63      99     -51      96      81    -113     -58     -76      40      80
-72      49     106     -50     -19     -20     -46    -122      17      17      23    -112     -60
-126      58     119      31     -23       5     -38     101     -50     -90     115      14       4
-25      83     -38      38     126      59      62      30     -74     -92       9     -22       7
-98     108      73     115      34      13     121    -112     115    -128     -10       4      21
 45      38     -28     -73    -121     -40    -112     110      66     -36     -75     -10    -128
-126      29      24     -93     -69     -46     120      90     119     -64      69     -64     -16
 75     -61    -104    -115     -19     113     -38     108      31      16      56     -55    -108
-77      51     112    -126      73     -61     -53     -62      27     -73     116     -24       7
 84     -80      41     -55     -65      76      33     118     -94      35     -63      81     -64
124      69      17      50     -45      80      28    -109     -12     -64      47     -76     -37
 50    -127      80     -21      45     -70      96     -38      45      95     120       2     -46
 11       2     -72     -75     -30     -16     -40      36     -63      69     -11      -7    -105
  4     -57      43      49      33      70     -36    -127     -33      16      86      16       5
107     -89    -128     103      16     -82    -117    -104     123    -126    -109       5     120
 42     -84    -102      27      88      85     118     126      82      69      89      17     -25
121     -44    -113     -83      98      46      17      81      17     -56     -38      93      49
 37      19     101    -114     -27      39      70      26      70    -107    -107    -109      21
-51    -104      46     -45      36      21      90      43       7     -67      43     -33     -78
103     -96      43     -60     -40     -69    -125     -67      92     -20     -57      73    -121
108      66     120      86     -24     -28      10     114     118     105     -72      27    -115
-83     -66      51     -57     -62      25      82     127      14      83       5     -92     -47
-126      35      54     123    -100     -77      80    -119     -45    -117     -72     107     -42
 29     112      33      74      60     -32     -73     118    -114      81    -117     -44    -121
 87      55      24      57      66      52     -46     105     -96     -12     104     -90      49
 60     103      82     126      11     -49     114     -44     -31     108     -34    -117
                
 Payload string decode as Base64, 12 per line
 -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1
 -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1
 -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1
 -1      -1      -1      -1      -2     -52      83      97      45     -12     123    -118     124
 -4      -8    -119     -14      19     -36      32      82    -111     -22     -79      46     -63
 39      21      85      99     -37     -85     -69      54      39    -105      64     -25       1
-52     -20      87    -105     118     -52     108     113     -45     -52      52    -118    -123
 27     114      36     -49       2      92      10     -86     -63    -126     105     -80      32
 14       6      37     -99     115      22      95      67     -47     -54    -104     119     -47
 18     -60     -94     -92     -93     -16      73     -39    -108      52     121     -83      40
103       5    -118     -25     100     -76     -24    -114      99    -110    -126      22     127
 66      57      21      88     -14     -63      92      48      93     -47      33      41      97
-96     -37     -33     -82     104     -43    -128     126      53      90      18      67      84
-27     -82     -50     -11      93     -62      92     -50      38      59     -53      80    -113
111      58     -92     -13     100    -116     -99     -11     -67      98      14      45     -25
-120    -111     -61      50     124      76     -60     -73    -117     -95     110      69     105
 99     -32      94      57     118      49      45     111     -66    -124     105      11     -98
 13      19    -127     107     -18     -39     -84    -127     -58      93       0    -118     -73
 40     -27     -55     -80       0    -115      94     122     -44      84     100     -42     -59
-12      25      47     119     -23     -11       6     102     -34    -126     -78    -120     -13
 77     -27      55    -111      71      58       9     -12      48     114     -40     124     119
-67      22      -8       7      24     -72      40      10      34     -31      11     -71     -37
-52    -110     104    -102     -60     -90    -102     -68     -97     -46      19     -67     -27
 68     -18       7      80    -110       4      46     -56     -14      67      33     -34      40
-19     -67     -46     104       2      31     -92      83     -10      60     -42       5      24
 -4     107      66    -123      11    -125      22     -84     -18     -34     -51      40      97
 17      17     121      12      72      35     -89     113      -2    -112      93     -90      92
-22     103      48     -32      78     117      61     -94     103     -29     -77     -31     -21
106      64     -98     -96     121     -26     -60    -105      50      32     -41    -103       7
 56      15      96      65      82     -46     110      75     120     125    -119       6     -28
 45     -53      95     104       8      33     -47    -118      59     -67      39    -123     -89
124       4      92      15       4     -68      57    -120     -34     -41      29     -90     -63
-15       3    -116    -103      75      51      55       8      36    -100      60     -68      33
-69     119      78    -128     117      75       2    -100    -101     -12     -62      23     106
 34      60      21      28       7     -60      81      19      45      53       1     -55      63
 76       2      -5      77     -77      40      21      14     -78     -37     -90      13     -94
-43      -9    -128      45      32     -80      43    -117      94      47      13    -126      76
 20      95      95    -103     112      76     114     -77      18      20     109     -56      29
-15       5      97       0      86     -70     120       6     113      10     -24     -71    -121
-72      41      48      87    -126     -86     -55     -95     -75    -123      87     103     -27
 36      85    -111      30     119     -99      72      -6     -42      34     -31      21      17
 28    -115     -91     -45      18      81      54      88     -18      82     116      97     -92
105      89      89      49      92     -39    -126     -19      50      65      85     -94     -80
123     -46     -67      -5      38     122       2     -68      77    -117     -72      59     -43
-50     -52     116    -104     118     -60      39    -123     110    -114      64     -89      39
102    -101    -127     -72     -38     -37     -29      60     124      33    -107      39     -16
-27      48      90      77      24      34      51     103     -71     -53      53       8     -99
 56     -69    -122     -67      97     -41       2      20     -93     -50      11     119     104
-27      24     -67      72     117     115     113    -125    -108      35      77      38    -102
 15      70    -118      99      19     -58     117      39     -32     -68      -9      45      78
 22     -51     -24     -76      -1      -1      -1      -1      -1      -1      -1      -1      -1
 -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1
 -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1                

Negatives? That's interesting. Two thoughts came to mind:

  1. Could they be glitches or corruption that's intentional?
  2. What happens if I remove anything out of the range and try to decode it to JP again?
  3. Maybe the byte data isn't signed and I should interpret them as unsigned numbers?
  4. Maybe it's not actually base64 decoded?

Idea one I couldn't really do anything with, but idea one was something I could probably verify without actually coding anything. If the negative data is just garbage to be tossed out, then I should be able to spot some escape character sequences if this is supposed to be some form of iso-2022-jp. The ESC character is 27 in decimal, and while there are a few in the above dumps, none of them are followed by a $ or a (, aka 36 or 40.

So that can't be it. I gave the corruption idea a try with this code:

// using the msg with padding removed 
byte rowByte;
byte colByte;
for (i = 0; i < decoded.length - 1; i++) {
    if (decoded[i] < 128 && decoded[i] >= 0) {
        rowByte = decoded[i];
    
        for (int j = i + 1; j < decoded.length; j++ ) {
            if (decoded[j] < 128 && decoded[j] >= 0) {
                colByte = decoded[j];
                System.out.println(String.format("0x%X%X", rowByte, colByte));
                for (String mapping : mappings) {
                    if (mapping.contains(String.format("0x%X%X", rowByte, colByte))) {
                        System.out.println(mapping);
                    }
                }        
                break;
            }
        }
    }
}

And while this did actually find a few mappings, it, much like when I put the data through the base 64 decoder online directly into iso-2022-jp, didn't really make anything new or obviously interesting.

So, this wasn't really getting me anywhere. I was chatting with someone in L:Q's discord server (a source of quite a bit of fun nerdiness) and accidently nerdsniped them as well. They were doing a number of analysis on it and concluding that it probably wasn't base 64 encoded given the way the padding was working out. So, we turned towards more analytical thinking. Using CyberChef I started experimenting.

It's a pretty awesome site, with a lot of information and Shannon scale suggested that it wasn't likely to be compressed or english text. Though when I base 64 decoded it:

The entropy rating jumped quite a bit! This matched with the nerd-sniped discord member's conclusions that the text had no grammatical structure. Though I did find it interesting that when I just dumped things out to iso-2022-jp and fed it to DeepL or Google Translate that sometimes I did get vaguely steinsgate sounding things:

The dirty work and the bamboo shoots which are controlled by bamboo presses are a major source of revenue for the Organization.

I got that when I saved the base64 decoded iso-2022-jp data to a .txt file and tried to translate it as it. The fact that there's a mention of "the Organization" does sort of feel like maybe my idea about discarding certain bad bits was on the right track, but it could just as easily be the translation software hallucinating and making something up considering that each time I fed some decoded data down into it, it felt like it changed the "meaning" of it.

Assuming that the data isn't text, I wondered if maybe there was some file magic that indicated a file type it should be. For example, I could take the data and write it out to a .wav file, .jpg, or similar and see what happens. Or, maybe this was a save file for the game itself! If I could crack open my own save file and understand the structure of it, perhaps there'd be something in that that would provide some sort of clue.

Looking at the save file it's a .DAT, which doesn't really say much. And given that it's my save file and I haven't finished the game yet, it's probably unlikely to contain... well, I suppose if I'm thinking that maybe the data is somehow related to a flag or something in the game, that I'd be able to find similar pieces in it somewhere. The only problem is that DAT files are just sort of generic binary files.

I have no reason to think this, but I think that's a dead end. But one thing I do know about Steins;Gate is that there's a pretty heavy focus on the IBM 5100 computer as a focal point. And, specifically, the IBM 5100 is effectively used as a way to encrypt data by the ever-present "Organization" that Rintaro faces.

I'm not sure if I think that this thing is a program for it, but I did find an emulator for the 5100 that could potentially be used to try to fiddle around and find out. I also found someone, (also a fan of the serieS) who acquired a real 5100. I'm not sure if they're trying to pursue the same idea as I am here, but they've probably got a better shot than I do since they've got the reference manual and other things on hand to dig for clues with.

Anyway, back to our payload problem. One interesting thing that wikipedia notes is this:

At the same time IBM announced the IBM 5100, it also announced the IBM 5100 Communications Adapter, which allowed the 5100 to transmit data to and receive data from a remote system. It made the 5100 appear the same as an IBM 2741 Communications Terminal and was designed to be able to communicate with IBM 2741 compatible machines in start-stop mode using the EBCD (Extended Binary Coded Decimal) notation,[16] referred to as PTTC/EBCD in IBM 2741 documentation

If this payload was sent from an IBM 5100 then perhaps we should try to decode the data using EBCD? If I look around for PTTC/EBCD, I find myself looking at this page And, an interesting, very suspicious thing on this page is:


                    NOTE: The / (slash) character is used with the PTTC/BCD and
                    PTTC/EBCD codes. A lower case x is used with the Correspondence code. 
                    However, in each case the transmission bit value (AC!) is the same.
All-call addressing (transmitting to all stations on the line simultaneously) is accomplished by the transmission of a special two-character code (/)

This... this seems kind of interesting because if we look at our data, we've got a bunch of slashes at the start and stop of the payload. This implies that this message could have been potentially sent out to all IBM stations in "All-call addressing" mode as the reference docs describe.

So... if we know that the message was sent out by an IBM machine, and the game itself keeps talking about "the hidden feature" of the computer, then perhaps we should look into Z-code? This is something that's mentioned by a user named voidstar in answering a question about the IBM 5100

There's a hex dump here... I wonder if the data that we've dumped out so far matches anything and pops anything useful out? Well, in the picture I can see 0xEC in Z code is 0xEB in EBCD. So, what's 0xEB? Assuming that it lines up with the international format, then its an empty character of . But if I look at the PTTC/EBCD table I... am very lost.

These [PTTC/EBCD coding] refer to the positioning of the characters around the typeball and, therefore, the tilt/rotate codes that have to be applied to the mechanism to produce a given character

So... I don't really see how we could do anything with this information. I don't have a physical machine to do anything with a type ball, and I'm not sure I understand how I'd translate a value from potential Z code into that in the first place. Assumedly if I was on the right track, then I suppose we'd decode the non / characters as an instruction on what to have the machine do or type and then maybe it would produce something of value? But also, maybe it would just produce incomprehensible gibberish, much like all our attempts to convert the payload from Base 64 to other encodings.

Future work

I hate to give up. But I'm definitely grasping as straws right now and unsure about where to go next. Also, I haven't finished the game yet. I've got 3 endings (I need to get what I assume to be a Kurisu ending, and a true ending and... maybe like, a bad end or something? I don't know), but I imagine that I might find more clues if I keep playing.

I assume that this email will do something at some point in the game, so maybe the behavior it exhibits at that point will help inform me if it's even possible to figure it out, or if the MAGES developers are just messing with me and every other potential person trying to crack the code.

That said, it was a lot of fun to explore and learning about character encodings, write code to examine and fiddle with binary a bit, and to get excited quite a few times on seeing something interesting happening with the data, even if I was ultimately thwarted for the time being. If you start examing or exploring this, I'd love to know, especially if you start figuring something out or find out that there is meaning within this high-entropy payload in Rintaro's phone! Swing by the stream if you do!